复制省略是一种巧妙的优化技术,在某些情况下,依赖复制省略实际上比"手动"传递引用更快。
因此,让我们假设您已经确定了一个关键的代码路径,在该路径中,您依赖于这样一个事实,即复制省略是由编译器为代码路径执行的,以获得最大性能。
但是现在您依赖于编译器优化。
是否有任何(显然是编译器特定的)方法来确保实际执行副本省略,并在无法执行副本省略时让编译器(或其他工具)生成警告/错误?
(我在想一些与Visual C++的__forceinline
非常相似的东西,如果编译器没有内联标记的函数,就会生成警告。)
否。
但是你可以写一个等价的,虽然完全不可读的代码:
BigObj f()
{
BigObj x(g());
x.someMethod();
return x;
}
//...
BigObj z = f();
//...
翻译(省略副本)为:
void f(BigObj* obj)
{
new(obj) BigObj(g());
obj->someMethod();
}
//...
char z[sizeof(BigObj)];
f((BigObj*)&z[0]);
//...
((BigObj*)&z[0])->~BigObj();
但说真的,只要以编译器可以消除副本的方式编写代码即可。即只返回一个没有分支的对象:
BigObj f()
{
BigObj x, y;
// use x and y
if(condition)
return x;
else
return y;
// cannot be elided
}
BigObj f()
{
if(condition)
{
BigObj x;
return x;
}
else
{
BigObj y;
return y;
}
// can be elided
}
不是真的,只是在复制构造函数中放入一个assert(false);
。
否则,请使用您最喜欢的探查器来衡量应用程序中有趣的部分是否足够快。
在C++1z(预计2017年)中,某些情况下需要保证副本省略:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
根据公共cppreference.com编译器功能支持wiki GCC 7+和Clang 4+,请确保这一点。
幸运的是,优化方面不需要启用新的语言支持,因为这是一个纯粹的优化(遵循旧的语言标准)。
此外,当应用优化时,允许复制构造函数不可用可能需要在编译期间启用较新的语言标准,或者使用不要求严格一致性的松散或扩展模式(例如,潜在的GCC的-fpermissive
)。
是否有任何(显然是编译器特定的)方法来确保复制省略实际上是执行的,并让编译器(或另一个工具)生成警告/错误执行?
gcc (trunk)
(尚未发布v14)具有-Wnrvo
";如果编译器没有在[class.copy.elision]允许的上下文中消除从局部变量到函数返回值的副本,则发出警告。这种省略通常称为命名返回值优化"
gcc (trunk)
在上可用https://godbolt.org/