我们说我有以下类型:
struct X {
X& operator+=(X const&);
friend X operator+(X lhs, X const& rhs) {
lhs += rhs;
return lhs;
}
};
我有声明(假设所有命名变量都是X
类型的左值):
X sum = a + b + c + d;
在C ++ 17中,我对这个表达式执行的副本和移动有多少保证?那么无保障的省略呢?
答案 0 :(得分:19)
这将执行1个复制构造和3个移动构造。
a
的副本以绑定到lhs
。lhs
移出第一个+
。+
的返回将绑定到第二个lhs
的值+
参数,并带有省略号。lhs
的返回将导致第二次移动建设。lhs
的返回将导致第三次移动建设。+
返回的临时内容将在sum
对于上述每个移动结构,存在可选地省略的另一个移动结构。所以你只有保证有1个副本和6个动作。但在实践中,除非你-fno-elide-constructors
,否则你将有1份副本和3次移动。
如果您在此表达式后没有引用a
,则可以进一步优化:
X sum = std::move(a) + b + c + d;
导致0个副本和4个移动(使用-fno-elide-constructors
进行7次移动)。
上述结果已通过X
确认,该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;
}
上的lhs开始重载:
+
这使事情变为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;
}
:
X&& x = a + b + c;
让您进行1次复印和1次移动。请注意,在此最新设计中,如果您的客户端执行此操作:
x
然后std::string
是一个悬空参考(这就是#svg-icons {
display: none;
}
.icon {
display: inline-block;
border: 1px solid red;
}
.icon + .icon {
margin-left: 20px;
}
不这样做的原因。)
答案 1 :(得分:9)
好的,让我们从这开始:
X operator+(X lhs, X const& rhs) {
lhs += rhs;
return lhs;
}
这将始终激活从参数到返回值对象的复制/移动。 C ++ 17并没有改变这一点,任何形式的省略都不能避免这种复制。
现在,让我们看一下表达式的一部分:a + b
。由于operator+
的第一个参数是按值获取的,因此a
必须复制。这是一份副本。返回值将被复制到返回prvalue中。所以这是1份副本和1份移动/复制。
现在,下一部分:(a + b) + c
。
C ++ 17表示从a + b
返回的prvalue将用于直接初始化operator+
的参数。这不需要复制/移动。但是,将从该参数复制此返回值。所以这是1份副本和2份移动/副本。
对最后一个表达式重复此操作,即1个副本和3个移动/副本。 sum
将从prvalue表达式初始化,因此不需要在那里进行复制。
您的问题似乎确实是中的参数是否仍然被排除在C ++ 17中的elision之外。因为they were already excluded in prior versions。这不会改变;从elision中排除参数的原因尚未失效。
&#34;保证省略&#34; 仅适用于到prvalues。如果它有名称,则不能为prvalue。