Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe for the input iterator and output iterator in std::transform to be from the same container?

Tags:

c++

stl

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?

like image 480
John Fitzpatrick Avatar asked Oct 05 '13 17:10

John Fitzpatrick


People also ask

Is input iterator moves sequentially forward yes or no give the reason?

Explanation: By definition Input iterators moves sequentially forward.

Which function is used to get a move iterator from a container?

Generally, like this: struct vec{ iterator begin() ; const_iterator begin() const; };

What does std :: transform do?

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) .


3 Answers

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 and binary_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 to first in case of unary transform, or to first1 or first2 in case of binary transform.

like image 166
jrok Avatar answered Oct 06 '22 16:10

jrok


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

like image 42
P0W Avatar answered Oct 06 '22 17:10

P0W


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.

like image 27
Duncan Smith Avatar answered Oct 06 '22 15:10

Duncan Smith