Sas*_*tin 3 c++ templates lvalue-to-rvalue move-assignment-operator c++14
我遇到一个问题,gcc编译器将局部变量(非临时)作为函数的rvalue参数移动.我有一个简单的例子:
class A
{
public:
A() {}
A& operator=(const A&) { std::cout << "const A&\n"; return *this; }
A& operator=(A&&) { std::cout << "A&&\n"; return *this; }
};
class B
{
public:
B() {}
B& operator=(const B&) { std::cout << "const B&\n"; return *this; }
B& operator=(B&&) { std::cout << "B&&\n"; return *this; }
template<class T> B& operator=(const T&) { std::cout << "const T& (T is " << typeid(T).name() << ")\n"; return *this; }
template<class T> B& operator=(T&&) { std::cout << "T&& (T is " << typeid(T).name() << ")\n"; return *this; }
};
int main(int argc, char **argv)
{
A a1;
A a2;
a1 = a2;
B b1;
B b2;
std::cout << "B is " << typeid(B).name() << "\n";
b1 = b2;
}
Run Code Online (Sandbox Code Playgroud)
输出:
const A&
B is 1B
T&& (T is 1B)
Run Code Online (Sandbox Code Playgroud)
我没想到它,因为移动赋值使rvalue为零.在我的情况下它导致崩溃,因为在b1 = b2之后使用了b2;
问题是它为什么会发生.
template<class T> B& operator=(T&&)
{ std::cout << "T&& (T is " << typeid(T).name() << ")\n"; return *this; }
Run Code Online (Sandbox Code Playgroud)
不是移动赋值运算符,因为它是一个模板.来自N4140,[class.copy]/19
甲用户声明的举动赋值运算符
X::operator=
是一个非静态非模板成员函数类X
型的正好一个参数X&&
,const X&&
,volatile X&&
,或const volatile X&&
.
您已经定义了一个带有转发引用的赋值运算符模板.在线
b1 = b2;
Run Code Online (Sandbox Code Playgroud)
该operator=(T&&)
模板比拷贝赋值运算符(更好的匹配B& operator=(const B&)
),因为T
会推导出B&
,并没有const
需要资格的转换.
如果typeid
用Boost.TypeIndex 替换对丢弃引用的调用,这就变得很明显了.
template<class T> B& operator=(T&&)
{
std::cout << "T&& (T is " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ")\n";
return *this;
}
Run Code Online (Sandbox Code Playgroud)
输出变为
const A&
B is B
T&& (T is B&)
Run Code Online (Sandbox Code Playgroud)
如果您不想operator=(T&&)
被选中,则可以约束它,以便从重载分辨率中删除它T=B
template<class T, std::enable_if_t<not std::is_same<B, std::decay_t<T>>{}, int> = 0>
B& operator=(T&&)
{
std::cout << "T&& (T is " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ")\n";
return *this;
}
Run Code Online (Sandbox Code Playgroud)
(您可能希望使用is_convertible
而不是is_same
涉及继承)