std::move 操作在 c++11 中的行为

Behaviour of std::move operation in c++11

本文关键字:move 操作 std c++11      更新时间:2023-10-16
#include <string>
#include <iostream>
#include <utility>
struct A {
    std::string s;
    A() : s("test") {}
    A(const A& o) : s(o.s) { std::cout << "move failed!n"; }
    A(A&& o) : s(std::move(o.s)) {}
    A& operator=(const A&) { std::cout << "copy assignedn"; return *this; }
    A& operator=(A&& other) {
        s = std::move(other.s);
        std::cout << "move assignedn";`enter code here`
        return *this;
    }
};
A f(A a) { return a; }
struct B : A {
    std::string s2;
    int n;
    // implicit move assignment operator B& B::operator=(B&&)
    // calls A's move assignment operator
    // calls s2's move assignment operator
    // and makes a bitwise copy of n
};
struct C : B {
    ~C() {}; // destructor prevents implicit move assignment
};
struct D : B {
    D() {}
    ~D() {}; // destructor would prevent implicit move assignment
    //D& operator=(D&&) = default; // force a move assignment anyway 
};
int main()
{
    A a1, a2;
    std::cout << "Trying to move-assign A from rvalue temporaryn";
    a1 = f(A()); // move-assignment from rvalue temporary
    std::cout << "Trying to move-assign A from xvaluen";
    a2 = std::move(a1); // move-assignment from xvalue
    std::cout << "Trying to move-assign Bn";
    B b1, b2;
    std::cout << "Before move, b1.s = "" << b1.s << ""n";
    b2 = std::move(b1); // calls implicit move assignment
    std::cout << "After move, b1.s = "" << b1.s << ""n";
    std::cout << "Trying to move-assign Cn";
    C c1, c2;
    c2 = std::move(c1); // calls the copy assignment operator
    std::cout << "Trying to move-assign Dn";
    D d1, d2;
//  d2 = std::move(d1);
}

执行语句a2 = std::move(a1)时,行为不同于执行语句b2 = std::move(b1)。在下面的语句中,b1.s在移动操作后不会变为空,而a1.s在移动操作后变为空。

谁能说出那里到底发生了什么?

关于 C++11 和右值引用的一个巨大(且持续)误解是std::move对对象(或该顺序上的某物)执行某些操作。

其实不然。 std::move实际上只是将其参数强制转换为 rvalue 引用类型并返回该类型。对对象执行的任何操作都发生在移动构造函数、移动赋值运算符(等)中,基于调用采用右值引用的版本(而不是采用值或左值引用的版本)的事实。

就你问的具体问题而言,至少根据你代码中的注释,你似乎有一些误解。关于a2=std::move(a1);的评论说您正在执行"从x值移动分配"。那是。。。充其量是误导。x值是立即被Xpire的值。这几乎是函数的返回值:

Foo &&bar() { 
    Foo f;
    // ...
    return f;
}

在这种情况下,bar()是一个xvalue,因为bar返回对对象的右值引用,该对象在函数完成执行时过期(超出范围)。

就您提出的具体问题而言,我怀疑这主要归结为您的标准库是否(如果是,确切地如何)实现std::string的移动构造函数的问题。例如,当使用g++(4.9.1)时,我得到的结果与您相同 - b1.s在用作移动源之前和之后都包含test。另一方面,如果我使用 MS VC++ 14 CTP,我会在移动前获得b1.s="test",在移动后b1.s=""。虽然我还没有测试过它,但我希望 Clang 的结果是一样的。简而言之,看起来 gcc 的标准库并没有真正实现std::string move赋值/构造(然而——至少从 v 4.9 开始——我还没有看过 5.0)。

通常移动赋值是作为std::string上的交换实现的,那么为什么字符串要变成空,因为它总是用"test"初始化?

你在哪里看到a1.s变得空,因为它没有印刷品?

我在这里没有看到任何奇怪的行为。两者的处理方式相同。