Bar*_*rry 18 c++ copy-elision c++17
假设我有以下类型:
struct X {
X& operator+=(X const&);
friend X operator+(X lhs, X const& rhs) {
lhs += rhs;
return lhs;
}
};
Run Code Online (Sandbox Code Playgroud)
我有声明(假设所有命名变量都是类型的左值X):
X sum = a + b + c + d;
Run Code Online (Sandbox Code Playgroud)
在C++ 17中,我对这个表达式执行的副本和移动有多少保证?那么无保障的省略呢?
How*_*ant 19
这将执行1个复制构造和3个移动构造.
a要绑定的副本lhs.lhs移出第一个+.+将使用elision 绑定到lhs第二个的by 参数+.lhs将导致第二次移动建设.lhs将导致第三次移动建设.+建筑将在sum.对于上述每个移动构造,存在可选地省略的另一移动构造.所以你只保证有1份副本和6次移动.但在实践中,除非你,否则你-fno-elide-constructors将获得1份副本和3份移动.
如果您a在此表达式后没有引用,则可以进一步优化:
X sum = std::move(a) + b + c + d;
Run Code Online (Sandbox Code Playgroud)
导致0个副本和4个移动(7个移动-fno-elide-constructors).
上述结果已经通过装备X复制和移动构造函数来确认.
更新
如果您对不同的优化方法感兴趣,可以从lhs开始重载X const&并开始X&&:
friend X operator+(X&& lhs, X const& rhs) {
lhs += rhs;
return std::move(lhs);
}
friend X operator+(X const& lhs, X const& rhs) {
auto temp = lhs;
temp += rhs;
return temp;
}
Run Code Online (Sandbox Code Playgroud)
这使事情减少到1副本和2个动作.如果您愿意限制您的客户+通过引用获取您的返回,那么您可以X&&从这样的重载之一返回:
friend X&& operator+(X&& lhs, X const& rhs) {
lhs += rhs;
return std::move(lhs);
}
friend X operator+(X const& lhs, X const& rhs) {
auto temp = lhs;
temp += rhs;
return temp;
}
Run Code Online (Sandbox Code Playgroud)
让你一举一动,一动.请注意,在此最新设计中,如果您的客户端执行此操作:
X&& x = a + b + c;
Run Code Online (Sandbox Code Playgroud)
然后x是一个悬空参考(这就是为什么std::string不这样做).
好的,让我们从这开始:
X operator+(X lhs, X const& rhs) {
lhs += rhs;
return lhs;
}
Run Code Online (Sandbox Code Playgroud)
这将始终激发从参数到返回值对象的复制/移动.C++ 17不会改变这一点,任何形式的省略都不能避免这种复制.
现在,让我们看一下你表达的一部分:a + b.由于第一个参数operator+是由值获取的,因此a必须将其复制到其中.这是一份副本.返回值将被复制到返回prvalue中.这是1份副本和1份移动/复制.
现在,下一部分:(a + b) + c.
C++ 17意味着返回的prvalue a + b将用于直接初始化参数operator+.这不需要复制/移动.但是,将从该参数复制此返回值.所以这是1副本和2个移动/副本.
对最后一个表达式重复此操作,即1个副本和3个移动/副本.sum将从prvalue表达式初始化,因此不需要在那里进行复制.
你的问题似乎真的是C++中的参数是否仍然被排除在elision之外17.因为它们已被排除在先前版本中.这不会改变; 从elision中排除参数的原因尚未失效.
"保证省略" 仅适用于prvalues.如果它有名称,则它不能是prvalue.