【问题标题】:Move constructors and rvalue references移动构造函数和右值引用
【发布时间】:2018-10-10 14:17:57
【问题描述】:
class Foo {
    int m_num;
public:
    Foo() {}

    Foo(int& n) {m_num = n;}

    Foo(int&& n) {m_num = n;}

    Foo(const Foo& src) {m_num = src.m_num;}

    Foo(Foo&& src) {m_num = src.m_num;}

    Foo& operator =(const Foo& src) {
        m_num = src.m_num;
        return *this;
    }

    Foo& operator =(Foo&& src) {// move
        m_num = src.m_num;
        return *this;
    }

    int& operator =(const int& src) {return m_num = src;}

    int& operator =(int&& src) {return m_num = src;}
};

为什么当我调用Foo f4(Foo(2)); 时它调用Foo(int&& n) 构造函数而不是Foo(Foo&& src) 构造函数? 另外为什么不传递作为右值引用调用移动构造函数的 num ? 例如,Foo f = num 不调用移动构造函数。

int main() {
    int&& num = 5;
    /*int num{ 5 };*/ // initializer-list
    Foo f = num; // custom constructor
    f = num; // assignment operator
    Foo f2 = 6; // custom move constructor
    f2 = 10; // custom move assignment
    f2 = f; // assignment operator
    Foo f3(f); // custom constructor
    // f3(f2); // ERROR
    f3 = (f2); // assignment operator

    Foo f4(Foo(2));

【问题讨论】:

    标签: c++ constructor move-semantics


    【解决方案1】:

    Foo(2) 中,整数是右值,需要来自int&& 的右值引用构造函数。

    万一

     Foo f = num; // custom constructor 
    

    不调用右值构造函数,因为命名值永远不会被视为右值。您必须致电std::move(num) 才能使其工作。

    标准以这种方式定义它,以避免在命名值被意外移动并在以后使用时造成混淆。即使变量是右值引用,您也必须明确说明移动。

    编辑

    根据cppreference,c++17 编译器必须省略的复制/移动构造函数(即在这种情况下它不能调用或要求存在复制/移动构造函数,这就是为什么你只看到构造函数来自int&&):

    Foo f4(Foo(2));
    

    这里引用 cppreference 的一段话(不如标准,但足够接近):

    语言规则确保不会发生复制/移动操作, 甚至在概念上:

    • 在初始化一个变量时,当初始化表达式 是与 变量类型:

      T x = T(T(T())); // 只调用一次 T 的默认构造函数,初始化 x

    【讨论】:

    • 感谢您的解释。你能解释一下为什么 Foo(2) 返回一个整数吗?我知道它是一个匿名对象,但在其构造函数主体中没有返回语句。
    • @sangminpark Foo(2) 不返回任何内容,只返回构造的 Foo 对象。 Foo(2) 使用 int&& 构造 Foo,并且省略了外部复制/移动构造函数。你可以写 Foo(Foo(Foo(Foo(2)))),你得到的唯一构造函数是 Foo(2)。所有其他复制/移动构造函数都被省略了。我不是语言律师,我不确定 c++17 是否规定了这种情况,但编译器无论如何都会这样做。通过简单地删除复制和移动构造函数并观察错误,很容易检查编译器是否认为复制省略是必需的。
    • 我手头没有要检查的标准,但我很确定即使省略副本,如果将副本 ctor 声明为已删除,代码也将无法编译带有“对已删除函数的引用”诊断。
    • @divinas 正如我所怀疑的那样,较新的 clang 和 gcc 保证在 C++17 模式下复制省略(尽管删除了复制 ctor),但在 c++14 模式下不保证。不知道标准是怎么说的
    • 根据 cppreference,在 C++17 中,这些复制/移动构造函数保证被忽略,并且不应该被要求存在。将此添加到答案中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    相关资源
    最近更新 更多