复制/移动省略需要复制/移动构造函数的明确定义

Al *_*bra 11 c++ copy-constructor move-semantics copy-elision c++17

考虑以下程序:

#include <iostream>
#include <utility>

class T {
public:
    T() { printf("address at construction:    %zx\n", (uintptr_t)this); }
    // T(const T&) { printf("copy-constructed\n"); } // helps
    // T(T&&) { printf("move-constructed\n"); }      // helps
    // T(const T&) = default;                        // does not help
    // T(T&&) = default;                             // does not help
};

T f() { return T(); }

int main() {
    T x = f();
    printf("address after construction: %zx\n", (uintptr_t)&x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译时g++ -std=c++17 test.cpp给出以下输出(与之相同clang++):

address at construction:    7ffcc7626857
address after construction: 7ffcc7626887
Run Code Online (Sandbox Code Playgroud)

基于C++引用,我希望程序输出两个相同的地址,因为应该保证复制/移动被省略(至少在C++ 17中).

如果我显式定义了复制或移动构造函数或两者(请参见示例中注释掉的行),程序会给出预期的输出(即使使用C++ 11):

address at construction:    7ffff4be4547
address after construction: 7ffff4be4547
Run Code Online (Sandbox Code Playgroud)

简单地设置复制/移动构造函数default没有帮助.

该引用明确指出

[复制/移动构造函数]不需要存在或可访问

那我在这里错过了什么?

xsk*_*xzr 9

因为这是一个特殊情况,复制省略可能不适用.

引自[class.temporary]第3段:

当类类型X的对象传递给函数或从函数返回时,如果X的每个复制构造函数,移动构造函数和析构函数都是微不足道的或删除的,并且X至少有一个未删除的复制或移动构造函数,则实现是允许创建一个临时对象来保存函数参数或结果对象.临时对象分别由函数参数或返回值构造,并且函数的参数或返回对象被初始化,就好像通过使用未删除的普通构造函数来复制临时对象(即使该构造函数不可访问或不会被选中)通过重载决策来执行对象的复制或移动).[注意:允许此纬度允许类类型的对象传递给寄存器中的函数或从寄存器中的函数返回. - 结束说明]

  • @AlGebra - 但是你的`T`不大,它非常小.在寄存器中构造并通过值返回的非常小的对象可能根本不需要任何堆栈空间.因此它可能*比涉及RVO机器*更有效*,这至少需要一些间接性. (3认同)