1

I have read a few posts about move functions (http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html for example), and I wanted to observe move operators in action. So I tried the following code:

#include <vector>
#include <cassert>
#include <functional>
#include <algorithm>
#include <iostream>
using namespace std;

vector<double> operator+(const vector<double>& a, const vector<double>& b){
  assert(a.size()==b.size());
  vector<double> result(a.size(),0);
    transform (a.begin(), a.end(), b.begin(), result.begin(), std::plus<double>());
  cout<<&result<<endl;
  return result;
}

int main(int argc, char const *argv[]) {
  vector<double> a,b;
  for (int i=0;i<10;i++){
    a.push_back(i);
    b.push_back(1);
  }
  std::vector<double> c=a+b;
  cout<<&c<<endl;
  return 0;
}

I was expecting to obtain the same address for the local variable result and c since move operators are implemented for vector. And I obtained exactly that, but with and without the flag -std=c++11. That is when I learned about NRVO (c++11 Return value optimization or move?), so I disabled it with the flag -fno-elide-constructors and now the address is different, even with the flag -std=c++11. Is there an issue with my code or did I understand something wrong about move operators ?

From what I understood, returning by value should be enough for the move operator to kick in (C++11 rvalues and move semantics confusion (return statement)).

PS: I tried with GCC 6.3.0_1 and Apple LLVM version 8.1.0 .

EDIT

As pointed out, I should have checked result.data() instead of &result (see below). But in that case, I always found the same address, even without std=c++11 and with -fno-elide-constructors. See accepted answer and its comment section.

Pierre Marchand
  • 619
  • 1
  • 7
  • 10

2 Answers2

3

The move constructor constructs a new object by stealing the resources of the old one. It does not merge temporaries at all: if the construction isn't elided, you still have two objects.

Quentin
  • 62,093
  • 7
  • 131
  • 191
2

Think of a move as an optimized copy. It is still a copy, so it is still a different vector but it has "moved" the underlying data from one vector to the other. You can see that by comparing the address of the data:

cout<<result.data()<<endl;

and

cout<<c.data()<<endl;

Copy elision on the other hand eliminates the copy entirely.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • 1
    Thanks for the quick answer. I tried with `data` and it worked. But I also obtain the same address with `-fno-elide-constructors` and without `-std=c++11`. It is strange no ? How can the compiler optimize it without copy elision and move operators ? – Pierre Marchand Jun 06 '17 at 09:18
  • @PierreMarchand in your question you said that `-fno-elide-constructors` always gave different addresses (which is what I would expect) – M.M Jun 06 '17 at 09:27
  • @M.M I think they are refering to the addresses of `.data()` – Chris Drew Jun 06 '17 at 09:28
  • @PierreMarchand Hmmm, [you are right](https://wandbox.org/permlink/UnXM5RkTsktC4aub) and I can't really explain it. I guess `fno-elide-constructors` promises to call the constructor but it doesn't promise that it will not make some optimization that reuses the same location in memory. – Chris Drew Jun 06 '17 at 09:38
  • @PierreMarchand OK, I think I can explain what is happening. I made [a little example](https://wandbox.org/permlink/3haTtP5LNIgGYoCl) to output every time a copy is made and you can see that with C++03 and `-fno-elide-constructors` there are actually two copies made. One from the local variable `result` to the temporary object returned from the function and a second copy from the temporary object to local variable `c` in the main function. And it just happens to copy the data from one place to another and back to where it started reusing the freed memory. – Chris Drew Jun 06 '17 at 10:20
  • thanks, but in conclusion it seems difficult to look at the action of a move operator on a STL container since you have to look at the address of the temporary object returned from the function to see that, without NRVO and move operators, it just goes back to the same address. – Pierre Marchand Jun 06 '17 at 10:28
  • @PierreMarchand Agreed, optimizations can be hard to observe. That is kind of the point, if they made significant observable changes in behavior they wouldn't be optimizations. My best advice is to compare performance and use a profiler to analyse. If time taken is not significant you don't care anyway and if it is significant you should see the effects of (N)RVO and move semantics. – Chris Drew Jun 06 '17 at 10:35