In this post one of the answers recommends changing a std::string
case this way:
std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
I've used it and it works so far in Visual Studio 2010. But is it guaranteed by the standard to always work? My concern is that I can imagine the possibility of implementations where writing to the output iterator (the third argument) could invalidate the input iterators (arguments one and two).
So, in summary, is the above method safe and portable?
Explanation: By definition Input iterators moves sequentially forward.
Generally, like this: struct vec{ iterator begin() ; const_iterator begin() const; };
std::transform applies the given function to a range and stores the result in another range, keeping the original elements order and beginning at d_first. 1) The unary operation unary_op is applied to the range defined by [first1, last1) .
Yes, this is guaranteed to be safe (as long as operation itself doesn't modify the elements or invalidate iterators).
From chapter [alg.transform]
from draft n3337:
template<class InputIterator, class OutputIterator,
class UnaryOperation>
OutputIterator
transform(InputIterator first, InputIterator last,
OutputIterator result, UnaryOperation op);
template<class InputIterator1, class InputIterator2,
class OutputIterator, class BinaryOperation>
OutputIterator
transform(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, OutputIterator result,
BinaryOperation binary_op);
2 Requires:
op
andbinary_op
shall not invalidate iterators or subranges, or modify elements in the ranges[first1,last1]
,[first2,first2 + (last1 - first1)]
, and[result,result + (last1 -first1)]
.[...]
5 Remarks:
result
may be equal tofirst
in case of unary transform, or tofirst1
or first2 in case of binary transform.
If you look into the first possible implementation of std::transform
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op)
{
while (first1 != last1) {
*d_first++ = unary_op(*first1++);
}
return d_first;
}
It may appear that its not "safe".
However, with std::transform(str.begin(), str.end(),str.begin(), ::toupper);
d_first
and first1
point to the same place, but they are not the same iterator !
There isn't any problem with incrementing both those iterators in a single statement.
Another implementation is some what like this (from MingW header file), which is equivalent, but looks bit cleaner
template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
UnaryOperation unary_op)
{
for (; first1 != last1; ++first1, ++d_first)
*d_first = unary_op(*first1);
return d_first;
}
Edited thanks to John Bartholomew
Yes, you can use the input iterator as the output iterator also, on a modifying algorithm it just means the modification will be done inline (on the source container) rather than on some other destination container.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With