正如标准所述,C++17 保证复制省略适用于作为参数的返回对象或临时对象。
但为什么不能两者兼而有之呢?如代码所示:
#include <print>
struct S {
S() { std::println("ctor"); }
S(const S &other) : data{other.data} { std::println("copy"); }
int data{42};
};
S make_s() { return {}; }
void add_1(S s) {
s.data++;
std::println("copy elided: {}", s.data);
}
S add_1_to(S s) {
s.data++;
return s;
}
void takeS(S s) { std::println("s.data: {}", s.data); }
int main() {
auto s0 = make_s();
std::println("copy elided: {}", s0.data);
add_1({});
auto s2 = add_1_to({});
std::println("copy not elided: {}", s2.data);
}
输出:
ctor
copy elided: 42
ctor
copy elided: 43
ctor
copy
copy not elided: 43
第3种情况
add_1_to({})
似乎只是前2种情况的组合,为什么复制省略没有发生?
因为这需要编译器在编译对函数的调用时查看函数的定义,以查看参数本身是否将被返回。
传统上,C 和 C++ 的设计使得编译器不必查看或查看函数的任何定义即可编译对其的调用。通常,函数的定义在同一翻译单元中甚至不可用。所有当前需要或允许的复制省略都是经过设计的,以便人们只需要知道调用站点的函数声明即可应用它们。话虽这么说,这可能是允许编译器使用的潜在有用的优化机会。有人必须写一篇论文向 C++ 标准委员会提出有关此问题的建议,并说服他们给予编译器此权限将是有益的。