保证省略和链接函数调用

时间:2017-02-10 18:04:17

标签: c++ c++17 copy-elision

我们说我有以下类型:

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中,我对这个表达式执行的副本和移动有多少保证?那么无保障的省略呢?

2 个答案:

答案 0 :(得分:19)

这将执行1个复制构造和3个移动构造。

  1. 制作a的副本以绑定到lhs
  2. 将构造lhs移出第一个+
  3. 第一个+的返回将绑定到第二个lhs的值+参数,并带有省略号。
  4. 第二次lhs的返回将导致第二次移动建设。
  5. 第三次lhs的返回将导致第三次移动建设。
  6. 第三个+返回的临时内容将在sum
  7. 构建

    对于上述每个移动结构,存在可选地省略的另一个移动结构。所以你只有保证有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。