【问题标题】:why gcc 6.4.0 c++14 moves automatically lvalue to rvalue为什么 gcc 6.4.0 c++14 自动将左值移动到右值
【发布时间】:2019-07-05 02:07:07
【问题描述】:

我遇到了 gcc 编译器将局部变量(不是临时的)作为右值参数移动到函数的问题。 我有一个简单的例子:

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;
}

输出:

const A&
B is 1B
T&& (T is 1B)

我没想到,因为移动赋值将右值归零。就我而言,它导致崩溃是因为在 b1=b2; 之后使用了 b2;

问题是为什么会发生。

【问题讨论】:

  • 我在您显示的代码中看不到任何“将右值归零”。请尝试创建minimal reproducible example 向我们展示。
  • b1=b2b2 后面的一行会被删除,那为什么不优化代码,用移动代替复制呢?在我看来这是一个聪明的优化。
  • 哦,如果您在代码崩溃方面需要帮助,请直接询问。也请阅读how to ask good questions,以及this question checklist
  • 阅读参考折叠和完美转发

标签: c++ templates c++14 move-assignment-operator lvalue-to-rvalue


【解决方案1】:
template<class T> B& operator=(T&&)
{ std::cout << "T&& (T is " << typeid(T).name() << ")\n"; return *this; }

不是移动赋值运算符,因为它是一个模板。来自 N4140,[class.copy]/19

用户声明的移动赋值运算符X::operator= 是类X 的非静态非模板成员函数,只有一个X&amp;&amp;const X&amp;&amp;、@ 类型的参数987654330@,或const volatile X&amp;&amp;

您已经定义了一个接受forwarding reference 的赋值运算符模板。在行中

b1 = b2;

operator=(T&amp;&amp;) 模板比复制赋值运算符 (B&amp; operator=(const B&amp;)) 更匹配,因为 T 将被推导出为 B&amp; 并且不需要 const 限定转换。

如果你用 Boost.TypeIndex 替换对 typeid 的调用,它会丢弃引用,这会变得很明显。

template<class T> B& operator=(T&&) 
{ 
  std::cout << "T&& (T is " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ")\n";
  return *this;
}

Live demo

输出变为

const A&
B is B
T&& (T is B&)

如果您不想选择 operator=(T&amp;&amp;),您可以对其进行约束,使其在 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; 
}

(如果涉及继承,您可能希望使用is_convertible 而不是is_same

Live demo

【讨论】:

  • 谢谢。不知道T&&&可以是
猜你喜欢
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-03
  • 2016-06-09
相关资源
最近更新 更多