开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜