这里不应该有一个copy ctor调用吗?禁用省略号(无命名 return 值优化)

Shouldn't there be a copy ctor invocation here? Elision disabled (no named return value optimization)

struct Test {
    int field = 30;
    Test() { cout << "In ctor" << endl; }
    Test(const Test &other) { field = other.field; cout << "In copy ctor" << endl; }
    Test(Test &&other) { field = other.field; cout << "In move ctor" << endl; }
    Test &operator=(const Test &other) { field = other.field; cout << "In copy assignment" << endl; return *this; }
    Test &operator=(Test &&other) { field = other.field; cout << "In move assignment" << endl; return *this; }
    ~Test() { cout << "In dtor" << endl; }
};

Test get_test() {
    Test t;
    return t;
}

int main() {
    Test t2 = get_test();
}

我认为这是典型的 NRVO 示例。我正在使用 -fno-elide-constructors 进行编译,我看到调用了以下内容:ctor、move ctor、dtor、dtor。

所以第一个ctor调用对应行Test t;,移动ctor在main中构造t2,然后是从get_test返回的临时被销毁,则main中的t2被销毁。

我不明白的是:按值返回时不应该有一个copy ctor调用吗?也就是说,我认为 get_test 应该制作 t 的副本,然后将此副本移至 t2。好像 t 马上就被移到了 t2

C++17

从 C++17 开始,mandatory copy elison 表示:

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

  • In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:

(强调我的)

这意味着 t2 是直接从 prvalueget_test return 构造的。由于 prvalue 用于构造 t2,因此使用了移动构造函数。请注意,在 C++17 中,标志 -fno-elide-constructorsreturn 值优化(RVO) 没有影响,并且与 NRVO 不同。


Pre-C++17

但在 C++17 之前,有 non-mandatory copy elison 并且由于您提供了 -fno-elide-constructors 标志,临时 prvalue 由 return 编辑=12=] 使用移动构造函数。所以你看到了对 move ctor 的第一次调用。然后,该临时文件用于使用移动构造函数再次初始化 t2,因此我们第二次调用移动构造函数。