【问题标题】:Is it possible to ensure copy elision?是否可以确保复制省略?
【发布时间】:2011-05-26 14:42:18
【问题描述】:

Copy elision 是一种简洁的优化技术,在某些情况下依赖于复制省略 can actually be faster 而不是“手动”传递引用。

因此,假设您已经确定了一个关键代码路径,您依靠编译器执行复制省略这一事实来获取代码路径以获得最佳性能。

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

是否有任何(显然是特定于编译器)方法来确保实际执行复制省略并让编译器(或其他工具)在无法执行复制省略时生成警告/错误?

(我正在考虑与 Visual C++ 的__forceinline 类似的东西,如果这样标记的函数没有被编译器内联,则会生成警告。)

【问题讨论】:

  • 自从提出并回答了这个问题以来,C++17 已在 GCC、Clang 和 MS VC++ 中发布和支持。考虑到这一点,您愿意更新它吗?
  • @PhilMiller - 不确定 question 在这方面有何显着变化。不过,我赞成你的回答。有用的信息。

标签: c++ diagnostics compiler-directives copy-elision


【解决方案1】:

不是真的,除了在复制构造函数中放一个assert(false);

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

【讨论】:

  • 嗯 ...在副本 operator 中带有 assert(false) 的想法似乎不错。对于标准容器和类似的东西,也许可以创建一个代理/包装对象,在其复制运算符中有一个断言。
  • 复制 [assignment] 运算符不影响按值传递。
  • @Mark:你是对的。断言已进入副本 constructor (因为这是我们要确保不调用的一件事)。
  • 您是否愿意根据 C++17 中采用的 P0135 更新或删除此答案?
【解决方案2】:

没有。

但是你可以写一个等价的,虽然完全不可读,代码:

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();

但是说真的,只要编写代码,让编译器可以忽略副本即可。 IE。只返回一个对象而不分支:

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
}

【讨论】:

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

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

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

根据communal cppreference.com compiler feature support wiki GCC 7+ 和 Clang 4+ 确保这一点。

幸运的是,优化方面不需要启用更新的语言支持,因为它是纯粹的优化(遵循旧的语言标准允许)。

当优化应用时允许复制构造函数不可用可能需要在编译期间启用较新的语言标准,或者使用不需要严格一致性的松散或扩展模式(例如可能是 GCC 的-fpermissive )。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 2023-01-30
    • 2020-06-30
    • 2016-01-27
    相关资源
    最近更新 更多