以下代码在使用或不使用用户定义的复制构造函数under 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,执行简单的浅副本),断言也不会失败,这意味着guaranteed copy elision按预期工作。
但是,如果没有任何用户定义的复制构造函数,则断言失败,这意味着s
函数中的对象main
不是默认构造的。为什么会这样?保证副本省略不在这里执行吗?
答案 0 :(得分:14)
引自C ++ 17工作草案§15.2临时对象第3段(https://timsong-cpp.github.io/cppwp/class.temporary#3):
当传递类类型X的对象或从函数返回时,如果X的每个复制构造函数,移动构造函数和析构函数是微不足道的或删除的,并且X至少有一个未删除的复制或移动构造函数,实现允许创建一个临时对象来保存函数参数或结果对象。 ... [注意:允许此纬度允许类类型的对象传递给寄存器中的函数或从寄存器中的函数返回。 - 结束记录]
在你的情况下,当我同时复制和移动构造函数时默认:
S(const S &) = default;
S(S &&) = default;
与GCC和Clang的断言也失败了。请注意,隐式定义的构造函数是微不足道的。