Reputation: 3271
I'd like to do a to_upper()
call on a substring, and thus pass in a range. I don't understand why I cannot construct a range on a string
, but can on a vector
in the same way.
Using Xcode 6.3.2 with compiler option -std=c++11
and Boost 1.58
void string_test() {
std::string my_str = "Hello, world, it's a wonderful day";
std::vector<int> x = {1,2,3,4,5,6,6,8,9,10};
const auto rng = boost::make_iterator_range(x.begin(),x.end()-5);
const auto rng2 = boost::make_iterator_range(my_str.begin(),my_str.begin()+10);
// How do I get to_upper_copy to accept the range returned above
std::cout << boost::to_upper_copy(rng2) << std::endl;
}
I get an error in Xcode:
/usr/local/Cellar/boost/1.58.0/include/boost/range/iterator_range_core.hpp:215:11: No matching constructor for initialization of 'std::__1::__wrap_iter<char *>'
Upvotes: 2
Views: 2880
Reputation: 3271
I finally realised after @Praetorian's answer that you can remove the smaller string allocation or vector copy completely by using the overloaded to_upper_copy() passing in the output iterator.
void string_algo() {
const std::string my_str = "Hello, world, it's a wonderful day";
const auto rng2 = boost::make_iterator_range(my_str.begin(),my_str.begin()+10);
boost::to_upper_copy(rng2.begin(),rng2);
std::cout << rng2 << std::endl;
}
Upvotes: 0
Reputation: 109119
The error doesn't have anything to do with creating an iterator_range
from string
; if you comment out the call to to_upper_copy
, your code will compile. Similarly, if you swap string
with vector<char>
(or even vector<int>
), the call to to_upper_copy
will again fail to compile with similar errors.
Clearly, the culprit is to_upper_copy
, and looking at the signature of the function template tells us why.
template<typename SequenceT>
SequenceT to_upper_copy(const SequenceT &,
const std::locale & = std::locale());
You cannot pass in a pair of string::iterator
s, which is what your make_iterator_range
call essentially returns , as SequenceT
because there would be no easy way for the algorithm to construct a new SequenceT
given a pair of iterators. This is described in the Boost documentation as well:
In addition some algorithms have additional requirements on the string-type. Particularly, it is required that an algorithm can create a new string of the given type. In this case, it is required that the type satisfies the sequence (Std §23.1.1) requirements.
In the reference and also in the code, requirement on the string type is designated by the name of template argument.
RangeT
means that the basic range requirements must hold.SequenceT
designates extended sequence requirements.
Note that the section referred to above is from the C++03 standard. In C++11 and later documents, the relevant section is §23.2.3 [sequence.reqmts].
So an iterator_range
does not satisfy the requirements of SequenceT
. If you pass my_str
instead to to_upper_copy
your code does compile. But if you want to only convert a sub-string, then you'll need to create a string
containing that, and then call to_upper
.
std::cout << boost::to_upper_copy(my_str) << std::endl; // works, but not a sub-string
std::string substr(rng2.begin(), rng2.end());
boost::to_upper(substr);
std::cout << substr << std::endl;
You can also avoid constructing the intermediate sub-string by using the to_upper_copy
overload that takes an output iterator argument.
std::string result;
result.reserve(rng2.size());
boost::to_upper_copy(std::back_inserter(result), rng2);
std::cout << result << std::endl;
Upvotes: 2