是否可能确保复制省略?

7

复制省略是一种巧妙的优化技术,在某些情况下,依靠复制省略实际上比手动传递引用更快

因此,假设您已经确定了一个关键代码路径,您依赖于编译器对代码路径执行复制省略以实现最大性能。

但现在您依赖于编译器优化。

是否有任何(特定于编译器的)方法可以确保实际执行复制省略,并让编译器(或其他工具)生成警告/错误,如果无法执行复制省略?

(我想到了与Visual C++的__forceinline类似的远程功能,如果标记为这样的函数未被编译器内联,则会生成警告。)


自从这个问题被提出并回答以来,C++17已经在GCC、Clang和MS VC++中发布并得到支持。你想根据这一点更新它吗? - Phil Miller
@PhilMiller - 不确定在这种情况下问题如何显著改变。不过我已经为你的答案点赞了。有用的信息。 - Martin Ba
4个回答

4

不行。

但你可以编写等效的代码,尽管它完全无法阅读:

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
}

1
@MSalters提到了一种巧妙的技术,可以允许返回不同的对象并仍然获得复制省略:如果条件为真,只需交换两者并始终返回第一个。 - Xeo
2
@Xeo:请注意,swap 只是一个特例。通常情况下,您可以通过在顶部添加 T return_value,将具有多个返回值的函数转换为单个返回值,将每个返回语句更改为对 return_value 的赋值,并最终返回 return_value。通常,这种模式可以进一步优化,例如通过使用合理的值初始化 return_value - MSalters

4

除了在复制构造函数中放置assert(false);外,没有其他方法。

否则,请使用您喜欢的分析器来测量应用程序的有趣部分是否足够快。


嗯...在复制操作符中使用assert(false)的想法似乎不错。对于std容器和类似的东西,也许可以编写一个代理/包装对象,在其复制操作符中加入assert。 - Martin Ba
复制[赋值]运算符不会影响按值传递。 - Mark B
@Mark:你说得对。断言应该放在复制构造函数中(因为这是我们想要确保不被调用的唯一函数)。 - Martin Ba
您是否考虑根据C++17中采用的P0135更新或删除此答案? - Phil Miller

2
在C++1z中(预计于2017年发布),某些情况将需要保证复制省略:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html

根据cppreference.com编译器特性支持维基,GCC 7+和Clang 4+可以保证这一点。
幸运的是,优化方面不需要启用更新的语言支持,因为它是纯粹的优化(遵循旧的语言标准允许)。
此外,当应用优化时,如果允许复制构造函数不可用,则可能需要在编译期间启用更新的语言标准,或者使用一个松散或扩展模式,不要求严格的符合性(例如,可能是GCC的 -fpermissive )。

1
有没有(显然是编译器特定的)方法可以确保实际执行复制省略,并且如果无法执行复制省略,则让编译器(或其他工具)生成警告/错误信息?
gcc(未发布的v14版)自带-Wnrvo选项。
"在[class.copy.elision]允许的情况下,如果编译器没有将局部变量的复制省略到函数的返回值中,则发出警告。这种省略通常称为命名返回值优化。"
可在https://godbolt.org/上获取gcc(trunk)版本。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接