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

Why is the C++11 move operator (=) behavior different

我已经在 C++11 中测试了 move Semantic。我用一个移动构造函数写了一个class。

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

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

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); 是构造,不是赋值。它完全类似于 C++11 之前的同类代码中的复制构造与赋值。

语法

 DefaultConstructor testConstructor2 = something;

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

这个:

T foo = bar;

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

T foo(bar);

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

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

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

而且他们都没有调用 DefaultConstructor::operator=