Move-CTOR和COPY-CTOR未调用



让我们进行以下C 样本:

#include <iostream>
struct X
{
  std::string s;
  X() : s("X") { }
  X(const X& other) : s{other.s} { std::cout << "cpy-ctorn"; }
  X(X&& o): s{o.s} { o.s = ""; std::cout << "move-ctorn"; }
  X& operator=(const X& other) {
    std::cout << "cpy-assignedn";
    s = other.s;
    return *this;
  }
  X& operator=(X&& other) {
    if (this != &other) {
      s = other.s;
      other.s = "";
    }
    std::cout << "move assignedn";
    return *this;
  }
};
X f(X x) {
  std::cout << "f: ";
  return x;
}
X g() {
  std::cout << "g: ";
  X x;
  return x;
}
int main() {
  X x;
  X y;
  x = f(X());
  y = g();
}

如果我用GCC 4.8.2编译,则有以下结果:

f: move-ctor
move assigned
g: move assigned

我不明白为什么我在调用G函数时未调用复制构建器。

我只是想了解何时调用副本或移动构造函数。

尽管您是正确的,可以从逻辑上确定返回g()的局部变量x的副本/移动,但是C 的有用功能是它可以 elide (即跳过(在许多情况下,即使复制/移动将具有副作用,也是如此。这是其中一种情况。执行时,这被称为命名的返回值优化。

可以说,它比我们移动语义之前的有用性要少,但是仍然很高兴。实际上,C 17在某些(选择(案例中必须进行强制性。

在C 中所有表达式均为:

  • lvalue
  • prvalue
  • xvalue

构建对象

-lvalue

Y x{};
Y y{x}; // copy constructor, x is an lvalue

-prvalue -RVO

使用RVO,默认情况下由GCC启用。这使用复制构造函数,而对象是一次构造的。

X g()
{
  X x {}; 
  x.value = 10;
  return x;
}
X y {g()}; // X constructor get's called only once to create "y". Also 
           // y is passed a a reference to g() where y.value = 10. 
           // No copy/move constructor for optimization "as if" rule

- prvalue -no rvo

没有RVO,在这种情况下,它取决于。如果移动构造函数被明确或隐式删除,则将调用复制构造函数

复制构造函数

struct X { X(const X&) {}}; // implicitly deletes move constructor 
                            // and move assignment, see rule of 5
X g()
{
  return X{}; // returns a prvalue
}
X y {g()}; // prvalue gets converted to xvalue, 
           // "temporary materialization", where the xvalue has an 
           // identity where members can be copied from. The xvalue
           // binds to lvalue reference, the one from copy constructor
           // argument

移动构造函数

X { X(X&&) {}}; // explicitly declared move constructor
X g()
{
  return X{}; // returns a prvalue
}
X y {g()}; // prvalue gets converted to xvalue, 
           // "temporary materialization", where the xvalue has an
           // identity where members can be moved from. The xvalue 
           // binds to rvalue reference, the one from move constructor 
           // argument

-xvalue

X x {};
X y {std::move(x)}; // std::move returns an xvalue, where if move 
                    // constructor is declared will call it, other wise 
                    // copy constructor, similar to explained above for 
                    // prvalue.

复制/移动分配

-lvalue

X x{};
X y{};
x = y; // call copy assignment operator since y is an lvalue.

-prvalue

如果移动分配是明确或隐式删除的,则将调用复制分配运算符。

复制分配

struct X{ X& operator=(const X&); } // implicilty deletes move 
                                    // constructor and move assignment,
                                    // see rule of 5
X g()
{
  return X{}; // returns a prvalue
}
x = g(); // prvalue gets converted to xvalue, 
       // "temporary materialization", where the xvalue has an identity 
       // where members can be copied from. The xvalue binds to lvalue 
       // reference, the one from copy assignment operator argument

移动分配

struct X{ X& operator=(X&&); } // explicitly declared move assignment        operator
X g()
{
  return X{}; // returns a prvalue
}
x = g(); // prvalue gets converted to xvalue, 
       // "temporary materialization", where the xvalue has an identity 
       // where members can be moved from. The xvalue binds to rvalue 
       // reference, the one from move assignment operator argument

-xvalue

X x {};
X y {};
x = std::move(x); // std::move returns an xvalue, where if move
                  // assignment is declared will call it, other 
                  // wise copy assignment, similar to explained 
                  // above for prvalue. 

当您使用同一类型的另一个对象实例化对象时,将调用复制构造函数。

例如:

X x;
X y(x);

代码中的最后一行分配了一个从函数返回到已经构造的对象的值。这是通过移动分配完成的。

最新更新