2

Given my understanding of return value optimization, I am a confused as to why the move constructor is being called in the sample code below:

#include <vector>
#include <iostream>

class MyCustomType
{
public:
  MyCustomType()
  {
    std::cout << "Constructor called" << std::endl;
  }

  MyCustomType(const MyCustomType & inOther) : // copy constructor
    mData(inOther.mData)
  {
    std::cout << "Copy constructor called" << std::endl;
  }

  MyCustomType(MyCustomType && inOther) : // move constructor
    mData(std::move(inOther.mData))
  {
    std::cout << "Move constructor called" << std::endl;
  }

private:
  std::vector<int> mData;
};

MyCustomType getCustomType()
{
  MyCustomType _customType;
  return _customType;
}

int main()
{
  MyCustomType _t = getCustomType();
}

Output:

Constructor called
Move constructor called

I assumed that there would only be a single instance of MyCustomType constructed and assigned directly to _t.

For info, I am using the VC14 compiler.

Harry
  • 1,362
  • 12
  • 19
  • 2
    Why did you assume that? Because of the C++ rules (which you would have misunderstood) or because of optimisations (which can be a bit unpredictable)? – BoBTFish Mar 22 '17 at 07:54
  • @BoBTFish Both, I assumed the C++ standard permitted single-construction in such a case and hoped the compiler would be smart enough to notice it. – Harry Mar 22 '17 at 07:58
  • If you only wanted a single instance constructed without any other constructor calls it would be `MyCustomType _t();` but in your post first a temporary instance is created `getCustomType()` and the assignment operator does not initiate a copy construction but a move construction. Which saves resources. C++ does not simplify your code that far, that it would convert those two statements. – Aeonos Mar 22 '17 at 08:08
  • 1
    See also,http://stackoverflow.com/questions/17473753/c11-return-value-optimization-or-move . And if you compile in release mode, MSVC will likely eliminate the move as well. Note also that the move constructor normally should take a non-const argument – nos Mar 22 '17 at 08:19
  • @Aeonos: You meant `MyCustomType _t{};` (as you have vexing parse: function declaration). – Jarod42 Mar 22 '17 at 08:42

2 Answers2

1

In your example you assume that NRVO will be applied. But NRVO is just an optimization, which is not guaranteed to be used by the compiler ^.

I have tested your example with http://webcompiler.cloudapp.net/.

The default set of compiler options there is:

/EHsc /nologo /W4

In such case the output is similar with yours:

Constructor called

Move constructor called

But if appropriate optimization is enabled with, say, /O2 flag:

/O2 /EHsc /nologo /W4

Then the output is:

Constructor called

^ As @einpoklum has mentioned in the comments, rules have changed since C++17.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
1

Because expression getCustomType() is a prvalue(rvalue), and parameter can be more cv-qualified than argument.

In order to see "more" copy elision, you can try release compiling mode.

BTW, inOther.mData isn't moved, mData(std::move(inOther.mData)) will in fact call vector's copy constructor.

felix
  • 2,213
  • 7
  • 16