It appears it is not the move ctor, nor the templated move ctor that is causing the problem, but the presence of enable_if<is_convertable<...
in the templated move ctor:
Testing with just an object, throwing auto
and pair
out of the test:
OK, copy/move elided:
cout << "# Construct Object: auto obj = LogMe();\n"; LogMe obj = LogMe(); LogMe(LogMe&&) { cout << __FUNCTION__ ... }
And, with a test like so:
cout << "# Construct Object: LogMeTempl obj = LogMeTempl();\n";
LogMeTempl obj = LogMeTempl();
cout << "# Construct Object: LogMeTempl obj2;\n";
LogMeTempl obj2;
OK, copy move also elided:
template<class Other> LogMeTempl(Other&& rhs // , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0 ) { cout << __FUNCTION__ << ...; }
Fail! Move ctor invoked!
template<class Other> LogMeTempl(Other&& rhs , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0 ) { cout << __FUNCTION__ << ...; }
And note that the enable_if can be reduced to
enable_if<true, void>::type** = 0
- if fact any additional defaulted parameter will do (e.g., int defaulted_param_on_move_ctor = 0
and it will still prevent the move elision).This also extends to a type with a copy-ctor only that has a default argument. It won't be elided either. A quick cross-check with gcc shows there doesn't seem to be any such problem there.
Short Answer
Types with defaulted arguments in their copy/move ctor don't have their initialization copy/move elided.
I have added a bug on MS.connect for this issue.
I have also added a test-case for (N)RVO to IDEone. Even without the default argument, *N*RVO seems to work better in gcc than VC++.