C++ 局部堆栈变量成员的返回值优化

C++ 局部堆栈变量成员的返回值优化,c++,reference,member-variables,return-value-optimization,C++,Reference,Member Variables,Return Value Optimization,我有一段通用代码,当实例化时,可以归结为: struct A{...}; A f1(){ A ret; std::pair<A&, int> p(ret, 1); p = g(); // g returns a pair<A, int>, but I am not interested in g return ret; // RVO :) }; 我理解,由于返回对象是模糊的,它不会执行RVO,或者编译器可能无法选择“优化”。 但

我有一段通用代码,当实例化时,可以归结为:

struct A{...};

A f1(){
    A ret;
    std::pair<A&, int> p(ret, 1);
    p = g(); // g returns a pair<A, int>, but I am not interested in g
    return ret; // RVO :)
};
我理解,由于返回对象是模糊的,它不会执行RVO,或者编译器可能无法选择“优化”。 但我看不出它不可能的根本原因,换句话说,我认为为了一致性,它应该做RVO(这是我想要的)

我之所以这样问,是因为我认为
f2
f1
更优雅

最后一个版本是否应该执行RVO?如果是,它是否依赖于编译器?

A f2(){
    std::pair<A, int> p;
    p = g();
    return p.first; // RVO ?
};

我使用gcc 8.1进行的粗略测试表明,RVO在以下
f2
f3
中不起作用:

struct A{
    double something;
    A() = default;
    A(A const& other) : something(other.something){std::cerr << "cc" << '\n';}
    A(A&& other) : something(other.something){std::cerr << "mc" << '\n';}
};

A f1(){
    A ret;
    std::pair<A&, int> p(ret, 1);
    p.first.something = 5.;
    return ret; // RVO :)
};

A f2(){
    std::pair<A, int> p;//(ret, 1);
    p.first.something = 5.;
    return p.first; // no RVO :(
};

A f3(){
    std::pair<A, int> p;//(ret, 1);
    p.first.something = 5.;
    return std::move(p).first; // no RVO :(
};


int main(){
    A a1 = f1(); // prints nothing, RVO
    A a2 = f2(); // prints "cc"; no RVO! Why?
    A a3 = f3(); // prints "mc" (no much gain in this case); still no RVO!
}
结构A{ 双重的东西; A()=默认值;
A(const&other):something(other.something){std::cerr要使RVO工作,返回值需要在调用方希望找到它的存储中实例化。这可能是一个由调用约定指定名称的寄存器,也可能是在堆栈上

std::pair
本质上是:

struct foo {
    A first;
    int second;
};

现在,如果返回值需要以
sizeof(a)
存储在特定的位置,但该对的大小较大,则该对不可能存储在该位置。如果被调用方知道
sizeof(int)值
在函数执行时,返回值后面的字节被允许被删除。但是在这种情况下,语言可能不需要RVO,因为它可能无法在每个调用约定中实现。

这种情况通常被称为NRVO
return std::move(p.first)这些规则是标准的,它是完美的,但是我只是好奇……C++标准通常不包括实现细节,但是它是用可实现的知识编写的(通常以现有的实现为证据)。。您希望的RVO可能无法在所有平台上实现。这是可以理解的。我想我可以添加一个案例细化,即返回的元素是类的第一个成员。因此它可以位于堆栈的开头。