Consider this minimal example:
include <iostream>
struct foo
{
foo &operator=(const foo &)
{
std::cout << "base copy assignment\n";
return *this;
}
foo &operator=(foo &&)
{
std::cout << "base move assignment\n";
return *this;
}
};
struct bar: foo
{
template <typename T>
bar &operator=(T &&x)
{
std::cout << "derived generic assignment\n";
foo::operator=(std::forward<T>(x));
return *this;
}
};
int main()
{
bar b, c;
b = c;
b = bar{};
}
The output of this program is:
derived generic assignment
base copy assignment
base move assignment
whereas I would expect it to be:
derived generic assignment
base copy assignment
derived generic assignment
base move assignment
Or, in other words, it seems like the perfect forwarding of operator=() in bar is not kicking in case of move assignment: the base move assignment operator is called instead.
I've tried the same test with a simple member function and in that case the result is as I would expect (i.e., the开发者_C百科 perfectly-forwarded assignment in bar is always called before any of the base assignments). Also, with GCC 4.5 as opposed to the 4.6 pre-release I'm currently using, the behaviour is also as I would expect.
Is this an intended behaviour specific to assignment operators or is this a recent GCC bug?
I at first answered incorrectly. I believe the output you are getting is correct. There is an implicit generation of a move assignment operator in bar, and that is what is getting called in your second assignment. The implicit move assignment operator is a better match than your explicit generic one.
There is also an implicit copy assignment operator, but in this case the generic assignment operator is a better match because c
is not const
.
Elaboration:
The generation of an implicit move assignment operator is only inhibited if the user declares a copy assignment operator, move assignment operator, copy constructor, move constructor or destructor. None of these special members can be a template. [class.copy] enumerates the forms for each of these special members.
For example a move assignment operator for class X
will have one of the following forms:
operator=(X&&);
operator=(const X&&);
operator=(volatile X&&);
operator=(const volatile X&&);
(any return type can be used).
The templated assignment operator in bar does not qualify as a special member. It is not a copy assignment operator nor a move assignment operator.
精彩评论