【问题标题】:Why is the C++11 move operator (=) behavior different为什么 C++11 移动运算符 (=) 行为不同
【发布时间】:2015-08-31 20:38:34
【问题描述】:

我已经在 C++11 中测试了移动语义。我写了一个带有移动构造函数的类。

class DefaultConstructor
{
public:
    DefaultConstructor(std::vector<int> test) :
        m_vec(std::forward<std::vector<int>>(test))
    {

    };

    DefaultConstructor(DefaultConstructor &&def) :
        m_vec(std::forward<std::vector<int>>(def.m_vec))
    {
    }

    DefaultConstructor& operator=(DefaultConstructor&& def) {
        m_vec = std::move(def.m_vec);
        return *this;
    }

    DefaultConstructor& operator=(const DefaultConstructor&) = delete;
    DefaultConstructor(DefaultConstructor &) = delete;

    std::vector<int> m_vec;
};

我编写了一个使用移动语义的主函数。我了解移动语义中发生了什么,它是一个很棒的工具。但是有些行为对我来说是无法解释的。当我为我调用主函数DefaultConstructor testConstructor2 = std::move(testConstructor); 时,应该调用DefaultConstructor&amp; operator=(DefaultConstructor&amp;&amp; def)。但是 Visual Studio 2015 调用移动构造函数。

int main()
{
    std::vector<int> test = { 1, 2, 3, 4, 5 };
    DefaultConstructor testConstructor(std::move(test));

    DefaultConstructor testConstructor2 = std::move(testConstructor);
    DefaultConstructor &testConstructor3 = DefaultConstructor({ 6, 7, 8, 9 });
    DefaultConstructor testConstructor4 = std::move(testConstructor3);
    swapMove(testConstructor, testConstructor2);
}

好吧,我想也许 = 移动运算符不再需要了。但我尝试了一个 SwapMove 函数。此函数调用 = move 运算符。

template<typename T>
void swapMove(T &a, T &b)
{
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

有人能解释一下这两个电话之间到底有什么区别吗? a = std::move(b);DefaultConstructor testConstructor2 = std::move(testConstructor); 的调用不应该具有相同的行为吗?

【问题讨论】:

  • DefaultConstructor testConstructor2 = std::move(testConstructor); 是初始化,不是赋值。
  • 事先单独声明testConstructor2,例如DefaultConstructor testConstructor2({});
  • Jonatha Potter 看起来这就是答案。谢谢。
  • [OT] 你想在你的移动构造函数中使用std::move(),而不是std::forward()
  • 没有什么要决定的,您已经明确表示def 是一个右值引用。 std::forward 仅在使用模板 + 引用折叠时有用。

标签: c++ c++11 move c++14 perfect-forwarding


【解决方案1】:

语法

 DefaultConstructor testConstructor2 = something;

总是调用构造函数,因为对象testConstructor2 还不存在。 operator= 只能在已经构建的对象的上下文中调用。

【讨论】:

    【解决方案2】:

    这个:

    T foo = bar;
    

    被称为copy-initialization。它通常但不总是等同于:

    T foo(bar);
    

    不同之处在于后者是对T的构造函数的直接函数调用,而前者试图构造一个从decltype(bar)T的隐式转换序列。因此存在直接初始化成功但复制初始化可能失败的情况。无论哪种方式,初始化都是初始化:它是构造函数调用,而不是赋值调用。

    在我们的例子中,这两行是完全等价的:

    DefaultConstructor testConstructor2 = std::move(testConstructor);
    DefaultConstructor testConstructor2{std::move(testConstructor)};
    

    他们都没有打电话给DefaultConstructor::operator=

    【讨论】:

    • 只是出于好奇,T foo = bar; 什么时候不等于T foo(bar)
    • @PCLuddite 当您需要多次转换时。例如:struct A { A(int ) { } }; struct B { B(A ) { } }; 鉴于此,B b(4); 成功但 B b = 4; 不会。
    • @PCLuddite 此外,当您有一个更好匹配的 explicit 构造函数时(或唯一可能的匹配,这会使 = 案例出错)。 struct A { explicit A(bool) { /* #1 */ } A(int) { /* #2 */ } }; A a = true; /* calls #2 */ A b(false); /* calls #1 */ 并且当相关类不可复制/移动时:struct B { B(B&amp;&amp;) = delete; B(int) {} }; B b(1); /* OK */ B b2 = 1; /* error */
    【解决方案3】:

    DefaultConstructor testConstructor2 = std::move(testConstructor); 是构造,而不是赋值。这与 C++11 之前的同类代码中的复制构造与赋值完全类似。

    【讨论】:

      猜你喜欢
      • 2012-09-20
      • 1970-01-01
      • 2010-12-29
      • 2011-04-19
      • 2012-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多