C++ templated method disambiguation rules with rvalue references
In the following example the ctors #1, #1, #4 and #4 are called (in that order). I would expect #1, #1, #2, #3 to be called instead (discounting RVO).
//-----------------------------------------------------------------------------
struct A
{
A(){} // 1
A(A&&){} // 2
A(const A&){} // 3
template<typename T>
A(T&&){} // 4
template<>
A(A&&){} // 5
template<typename T>
A(const T&){} // 6
};
//-----------------------------------------------------------------------------
A wtf(){ A x; return x; }
//-----------------------------------------------------------------------------
int main( int, char*[]开发者_Python百科 )
{
A a;
A c = wtf();
A b(c);
}
What's going on and why?!
Note: Remove #5 to compile with GCC (it's not that important anyway) - the above does compile with VS2010. I would be interested to hear if the results are the same in GCC if anyone can test it.
This is a compiler bug.
Forgetting discussions about implicit rvalues for return local
expressions (and throw local
expessions too, by the way), this is clearly a compiler bug because --
none of the templated constructors should have been called in your example code.
No template function can be considered a copy constructor, copy assignment operator, move constructor, or move assignment operator.
C++11 support is patchy, although gcc's is the best it's still incomplete and buggy.
You are having way too many constructors for a real class. This doesn't happen except in test code.
Constructor #4 with T
being A&
, and A& &&
collapsing to A&
, would be a better match for A b(c)
than either #2 (c
is not an rvalue) or #3 (c
is not const).
return x;
does not call the move constructor because x
might not be expiring. In general terms it's just an lvalue like any other. return std::move( x );
is usually necessary. (EDIT: but see next paragraph.)
There is a special rule that requires the compiler to perform a move operation instead of copy elision if a potential elision is not performed, §12.8/32. I'm not sure why you're not seeing that. Perhaps the compiler overlooked that rule because they don't expect the user to turn off elision.
You might expect the copy constructor A( A const & )
to perform the return
copy, but the template A( T && )
implements perfect forwarding. T &&
is deduced to A &
, which is not const
and therefore a better match.
Be very careful with perfect forwarding combined with copy constructors. I'm a bit surprised the language doesn't special-case this. As far as I know, the solution is to use SFINAE to disable the template under that circumstance.
精彩评论