8

Both string_ref in boost and string_span in GSL doesn't define constructor that takes a pair of iterator. What is the reason of this decision ?

Usually it's not a big deal, I can just create string_ref like this :

boost::string_ref s(start, std::distance(start, finish));

but the reason I want constructor that take a pair of iterators is because I have code that look like this:

template<typename Type, typename Iterator>
void func(const Iterator& begin, const Iterator& end)
{
    Type s(begin, end);
    //do stuff with s
}

Currently, I can call it like this :

func<std::string>(start, finish)

I want to change it to :

func<boost::string_ref>(start, finish) //compile error

but that code won't compile because the lack of constructor taking a pair of iterator in string_ref

Kamil Zubair
  • 229
  • 2
  • 10
  • I'm assuming that your iterators are in fact `std::string::iterator` ? Because `string_ref` references an existing string, so you can't construct it from thin air. – MSalters Nov 17 '15 at 13:59
  • @MSalters Actually, my iterator are already `boost::string_ref::iterator` :). – Kamil Zubair Nov 17 '15 at 15:29
  • A worth reading chat about this was done at the std future proposal group: https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/6X6_IjfzdYI , worth the read. – Dam Mar 19 '17 at 12:38

3 Answers3

5

boost::string_ref is a simple reference to a string in the form of a pointer to a contiguous block of memory with a predefined length. Since iterators are much more generic, you cannot assume that your start, finish range refers to anything like a contiguous block of memory.

On the other hand, a std::string can be constructed from a range defined by Iterators because it will simply make a copy of the range's values, regardless of what the underlying data structure is.

matz
  • 626
  • 6
  • 18
  • 4
    That's not actually a wholly convincing argument. It would be reasonable to construct a `boost::string_ref` from a `std::string::iterator`. But even that is not allowed. – MSalters Nov 17 '15 at 13:57
  • @MSalters Exactly, at the very least they should be able to accept a pair of `boost::string_ref::iterator` – Kamil Zubair Nov 17 '15 at 15:35
2

Helper function I created, hopefully someone else can find this useful. Briefly tested MSVC14/boost 1.59, MSVC17/boost 1.64, MSVC17/C++17

#include <boost/utility/string_ref.hpp>

// todo:  change to std::basic_string_view<charT> in C++17
template <typename charT> using basic_string_view_type = boost::basic_string_ref<charT>;    

// Creates a string view from a pair of iterators
//  http://stackoverflow.com/q/33750600/882436
template <typename _It>
inline constexpr auto make_string_view( _It begin, _It end )
{
    using result_type = basic_string_view_type<typename std::iterator_traits<_It>::value_type>;

    return result_type{
        ( begin != end ) ? &*begin : nullptr
        ,  (typename result_type::size_type)
        std::max(
            std::distance(begin, end)
            , (typename result_type::difference_type)0
        )
     };
}   // make_string_view
Tom
  • 1,977
  • 1
  • 20
  • 19
  • I just fell into the corner case: if `begin == end` and they are not dereferenceable (i.e. they both point at the end of a string) then `&*begin` causes UB. – Quentin Aug 29 '18 at 09:27
  • @Quentin -- good catch, I updated my post to correct that issue, thank you. Now it's just returning an empty string view when begin==end – Tom Aug 30 '18 at 21:15
1

Looks like I make a mistake. gsl::string_span do have a constructor that takes begin and end iterator. So, there is nothing problematic with creating string_view from iterator pairs and the lack of it in boost::string_ref is probably just an oversight.

For my case, I end up inheriting from boost::string_ref and add the constructor myself.

Kamil Zubair
  • 229
  • 2
  • 10