为什么C++ 11移动运算符(=)行为不同

Pet*_*rNL 2 c++ move perfect-forwarding c++11 c++14

我已经测试了C++ 11中的Semantic移动.我用移动构造函数编写了一个类.

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;
};
Run Code Online (Sandbox Code Playgroud)

我写了一个使用移动语义的主函数.我理解移动语义中发生的事情,它是很棒的工具.但是有些行为对我来说是不可解释的.当我打电话DefaultConstructor testConstructor2 = std::move(testConstructor);给我的主要功能DefaultConstructor& operator=(DefaultConstructor&& 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);
}
Run Code Online (Sandbox Code Playgroud)

好吧,我想也许不再需要= Move Operator了.但我尝试了一个SwapMove功能.此函数调用= move运算符.

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

有人可以解释这两个电话的区别究竟是什么?不应该是电话a = std::move(b);并且DefaultConstructor testConstructor2 = std::move(testConstructor);具有相同的行为?

zda*_*dan 8

语法

 DefaultConstructor testConstructor2 = something;
Run Code Online (Sandbox Code Playgroud)

总是调用构造函数,因为该对象testConstructor2尚不存在.operator =只能在已构造的对象的上下文中调用.


Bar*_*rry 6

这个:

T foo = bar;
Run Code Online (Sandbox Code Playgroud)

称为复制初始化.它通常(但不总是)等同于:

T foo(bar);
Run Code Online (Sandbox Code Playgroud)

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

在我们的例子中,这两行完全相同:

DefaultConstructor testConstructor2 = std::move(testConstructor);
DefaultConstructor testConstructor2{std::move(testConstructor)};
Run Code Online (Sandbox Code Playgroud)

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

  • @PCLuddite当您需要多次转换时.例如:`struct A {A(int){}}; struct B {B(A){}};`给定,`B b(4);`成功但是`B b = 4;`不会. (3认同)
  • @PCLuddite当你有一个`explicit`构造函数是一个更好的匹配(或唯一可能的匹配,这使得`=`case一个错误).`struct A {explicit A(bool){/*#1*/} A(int){/*#2*/}}; A =真;/*调用#2*/A b(假);/*调用#1*/`并且当有问题的类不可复制/移动时:`struct B {B(B &&)= delete; B(int){}}; B b(1);/*OK*/B b2 = 1;/*错误*/` (2认同)