为什么gcc 6.4.0 c ++ 14会自动左移lvalue到rvalue

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;

问题是它为什么会发生.

Pra*_*ian 5

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涉及继承)

现场演示