使用if语句移动构造函数,但使用三元运算符复制构造函数

时间:2017-09-08 01:54:17

标签: c++ c++11 move-semantics

上下文:我正在做实验,以了解gcc何时执行RVO,如果没有,何时使用移动语义。我的gcc版本是g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)

问题:我有一个按值返回Foo的函数。编译器无法执行RVO,因为有两个可能的命名返回值。当我使用三元运算符选择返回Foo中的哪一个时,我需要显式调用std::move以避免复制。使用if语句时我不需要std::move。为什么会出现这种差异?

代码:

#include <iostream>

using namespace std;

struct Foo {
    std::string s;
    Foo()                                        { cout << "Foo()\n"; }
    ~Foo()                                       { cout << "~Foo()\n"; }
    Foo(const Foo& other)     : s(other.s)       { cout << "Foo(const Foo&)\n"; }
    Foo(Foo&& other) noexcept : s(move(other.s)) { cout << "Foo(Foo&&)\n"; }
};

Foo makeFooIf(bool which) {
    Foo foo1; foo1.s = "Hello, World1!";
    Foo foo2; foo2.s = "Hello, World2!";
    if (which) return foo1;
    else       return foo2;
}

Foo makeFooTernary(bool which) {
    Foo foo1; foo1.s = "Hello, World1!";
    Foo foo2; foo2.s = "Hello, World2!";
    return which ? foo1 : foo2;
}

Foo makeFooTernaryMove(bool which) {
    Foo foo1; foo1.s = "Hello, World1!";
    Foo foo2; foo2.s = "Hello, World2!";
    return which ? move(foo1) : move(foo2);
}

int main()
{
    cout << "----- makeFooIf -----\n";
    Foo fooIf = makeFooIf(true);
    cout << fooIf.s << endl;

    cout << "\n----- makeFooTernary -----\n";
    Foo fooTernary = makeFooTernary(true);
    cout << fooTernary.s << endl;

    cout << "\n----- makeFooTernaryMove -----\n";
    Foo fooTernaryMove = makeFooTernaryMove(true);
    cout << fooTernaryMove.s << endl;

    cout << "\n----- Cleanup -----\n";
    return 0;
}

输出:

----- makeFooIf -----
Foo()
Foo()
Foo(Foo&&)
~Foo()
~Foo()
Hello, World1!

----- makeFooTernary -----
Foo()
Foo()
Foo(const Foo&)
~Foo()
~Foo()
Hello, World1!

----- makeFooTernaryMove -----
Foo()
Foo()
Foo(Foo&&)
~Foo()
~Foo()
Hello, World1!

----- Cleanup -----
~Foo()
~Foo()
~Foo()

1 个答案:

答案 0 :(得分:1)

在某些情况下有一个隐含的举动:

<强>§12.8.32

  

当满足复制/移动操作的省略标准时,但是   不是用于异常声明,而是要复制的对象是   由左值指定,或在返回语句中表达式   是一个(可能带括号的)id-expression ,用于命名对象   自动存储持续时间在体内声明或   最里面封闭函数的参数声明子句或   lambda-expression,用于选择构造函数的重载决策   首先执行复制,好像对象是由a指定的   的右值即可。如果第一个重载决策失败或未执行,   或者,如果所选构造函数的第一个参数的类型是   不是对象类型的右值引用(可能是cv限定的),   再次执行重载决策,将对象视为   左值。

我的咆哮