17

以下代码在 GCC 8.0.1 下使用或不使用用户定义的复制构造函数的行为不同:

#include <cassert>

struct S {
    int i;
    int *p;
    S() : i(0), p(&i) {}
    // S(const S &s) : i(s.i), p(&i) {}  // #1
    // S(const S &s) : i(s.i), p(s.p) {} // #2
    // S(const S &s) = delete;           // #3
};

S make_S() {return S{};}

int main()
{
    S s = make_S();
    assert(s.p == &s.i);
}

使用任一注释的用户定义的复制构造函数(即使使用#2,执行简单浅拷贝的构造函数),断言都不会失败,这意味着保证复制省略按预期工作。

但是,如果没有任何用户定义的复制构造函数,则断言失败,这意味着函数中的对象s不是main默认构造的。为什么会这样?不保证复制省略在这里执行吗?

4

1 回答 1

16

引用 C++17 工作草案 §15.2 临时对象第 3 段(https://timsong-cpp.github.io/cppwp/class.temporary#3):

当类类型 X 的对象被传递给函数或从函数返回时,如果 X 的每个复制构造函数、移动构造函数和析构函数都是平凡的或已删除的,并且 X 至少有一个未删除的复制或移动构造函数,则实现是允许创建一个临时对象来保存函数参数或结果对象。... [ <em>注意:授予此权限以允许将类类型的对象传递给寄存器中的函数或从函数返回。——<em>尾注]

在您的情况下,当我将复制和移动构造函数都设为默认值时:

S(const S &) = default;
S(S &&) = default;

GCC 和 Clang 的断言也失败了。请注意,隐式定义的构造函数是微不足道的。

于 2018-02-20T09:02:53.787 回答