1

SUMMARY

I stumbled upon the implementation of std::transform and I could not figure out why it works in the case where we transform a container in-place. In particular, I found this expression confusing *d_first++ = unary_op(*first1++);.

CODE

Using the first implementation of std::transform found here https://en.cppreference.com/w/cpp/algorithm/transform, I wrote this sample code:

#include <iostream> 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; } int main() { int arr[5] {1,2,3,4,5}; transform(std::begin(arr),std::end(arr), std::begin(arr), [](auto i){return i*i;}); for (auto& elem : arr) std::cout << elem << std::endl; return 0; } 

ATTEMPT FOR AN EXPLANATION

After doing some reading in the C++ standard, Cpp reference, and SO, I think I might be able to explain what happens in that confusing (for me) statement. Let's decompose this expression:

*d_first++ = unary_op(*first1++); // same as A = B; 
  1. First resolve what is happening on B, which is, unary_op(*first1++)
  2. Now resolve the parentheses *first1++ which means *(first1++) which means a) make a copy of the dereferenced first1 and b) pass it to the function, then c) increment the iterator.
  3. The copied dereferenced first1 is multiplied by itself inside the unary_op.
  4. Now B is evaluated, time to deal with the assignment.
  5. Assign B to the dereferenced value of d_first.
  6. When this assignment is complete, and before we go to the next statement (whatever that may be) increment the d_first iterator.

This way, the two iterators are "on the same page", meaning at the same position of the arrays which they iterate.

QUESTIONS

My questions are:

A) Is the above explanation correct?

B) Which operations in that expression are evaluations and which computations?

C) Since *first1++ translates to *(first1++) but the dereferencing happens before the incrementation, isn't that confusing to remember? However, (*first1)++ would mean something totally different, i.e. dereference first1 and pass it to the function and then increment this value by one; not the iterator! Any hints to remember how to untangle myself in such expressions?

4
  • 5
    "but the dereferencing happens before the incrementation" - no, it doesn't. The increment is done first, but a copy of the old value is "returned" by the postfix ++. It's that copy that gets dereferenced. Commented Jan 5, 2022 at 15:24
  • 2
    "but the dereferencing happens before the incrementation". It is not pedantically true. deferencing happens after the increment, but on the value returned by first1++ which is the one before the incrementation. Commented Jan 5, 2022 at 15:33
  • "make a copy of the dereferenced first1" The copy is made only because the function receives the parameter by value. Commented Jan 5, 2022 at 15:34
  • I think OP meant "happens before the incrementation" in a 'it comes first when reading left-to-right' sense. Commented Jan 5, 2022 at 15:35

1 Answer 1

2

Just answering C).

Any hints to remember how to untangle myself in such expressions?

*i++, *dst++ = do_something_with(*src++);, etc., are well-established patterns in C++ and C. Therefore, the suggestion is:

  • If you mean *(first++), write it as *first++, because it is the least surprising. (And get used to reading this expression the right way.)

  • If you mean (*first)++, then write it as such. (You have no choice anyway.) The presence of the parentheses indicates immediately, that a meaning is intended that is different from *first++.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.