C++ 保证省略和链式函数调用

C++ 保证省略和链式函数调用,c++,c++17,copy-elision,C++,C++17,Copy Elision,假设我有以下类型: struct X { X& operator+=(X const&); friend X operator+(X lhs, X const& rhs) { lhs += rhs; return lhs; } }; 我有一个声明(假设所有命名变量都是X类型的左值): 在C++17中,对于这个表达式将执行多少拷贝和移动,我有什么保证?那么非保证省略呢?好的,让我们从以下内容开始: X operato

假设我有以下类型:

struct X {
    X& operator+=(X const&);
    friend X operator+(X lhs, X const& rhs) {
        lhs += rhs;
        return lhs;
    }
};
我有一个声明(假设所有命名变量都是
X
类型的左值):


在C++17中,对于这个表达式将执行多少拷贝和移动,我有什么保证?那么非保证省略呢?

好的,让我们从以下内容开始:

X operator+(X lhs, X const& rhs) {
    lhs += rhs;
    return lhs;
}
这将始终引发从参数到返回值对象的复制/移动。C++17并没有改变这一点,任何形式的省略都无法避免这种复制

现在,让我们看一下表达式的一部分:
a+b
。由于
operator+
的第一个参数是按值获取的,因此必须将
a
复制到其中。这是一份。返回值将被复制到返回值中。这就是一个拷贝和一个移动/拷贝

现在,下一部分:
(a+b)+c

C++17表示从
a+b
返回的PR值将用于直接初始化
运算符+
的参数。这不需要复制/移动。但此参数的返回值将从该参数复制。这就是1个拷贝和2个移动/拷贝

对最后一个表达式重复此操作,即1个副本和3个移动/副本
sum
将从prvalue表达式初始化,因此不需要在那里进行复制


您的问题似乎是,在C++17中,参数是否仍然被排除在省略之外。因为。这不会改变;从省略中排除参数的原因尚未失效


“保证省略”仅适用于PRV值。如果它有名称,则不能是prvalue。

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

  • 复制
    a
    以绑定到
    lhs
  • 将结构
    lhs
    移出第一个
    +
  • 第一个
    +
    的返回将通过省略绑定到第二个
    +
    的by值
    lhs
    参数
  • 第二个
    lhs
    的返回将导致第二个移动构造
  • 第三个
    lhs
    的返回将产生第三个移动构造
  • 从第三个
    +
    返回的临时值将在
    sum
    处构造
  • 对于上面描述的每个移动构造,有另一个移动构造可选地被省略。所以你只能保证有1个副本和6个动作。但在实践中,除非您
    -fno elide构造函数
    ,否则您将有1个副本和3个移动

    如果在此表达式之后未引用
    a
    ,则可以进一步优化:

    X sum = std::move(a) + b + c + d;
    
    导致0次拷贝和4次移动(使用
    -fno-elide构造函数执行7次移动)

    上述结果已由
    X
    确认,该X已检测复制和移动构造函数


    更新

    如果您对优化此功能的不同方法感兴趣,可以从重载
    X const&
    X&&
    上的lhs开始:

    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;
    }
    
    这使事情下降到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;
    

    然后
    x
    是一个悬空引用(这就是为什么
    std::string
    不这样做的原因).

    其中一些将是移动-你的意思是1次复制和3次移动?以不同的方式表达Nicol对参数复制的评论:按值获取参数只会有助于删除复制,并且只有在使用参数时才有意义,即,
    std::move()
    将其放在其他地方,例如:
    xrc(std::move(lhs));rc+=rhs;返回rc
    @Barry:是的,我忘了从返回局部变量开始的移动是自动的。我想如果这整件事可以编译成相当于
    X sum(a);总和+=b;总和+=c;总和+=d猜测这仍然很难。所以没有省略链?e、 g.将
    a+b
    的返回直接构造成
    operator+(?,c)
    ?我甚至认为省略
    lhs的返回是不合法的。然而,我也不认为任何编译器的作者有任何动机使它合法化(无论如何没有人试图这样做)。为什么它可能是非法的?它将来会合法吗?@Orient:我想说的是标准不允许这样做。据我所知,从来没有人提出过。我怀疑原因是没有人知道如何实现它。但这些都是猜测,我不是编译专家。
    
    X&& x = a + b + c;