jeudi 7 septembre 2017

Move constructor with if-statement but copy constructor with ternary operator

Context: I was making experiments to learn when does gcc perform RVO, and if not, when does it use move semantics. My version of gcc is g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4).

Question: I have a function that returns a Foo by value. Compiler cannot perform RVO because there are two possible named return values. When I use the ternary operator to select which of Foo to return, then I need to explicitly call std::move to avoid the copy. I do not need the std::move when using an if statement, I do not need to. Why the discrepancy?

Code:

#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;
}

Output:

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

Aucun commentaire:

Enregistrer un commentaire