arguments to tuple are copied instead of moved when returning the tuple from a function

Viewed 618

I have a question about the following code. My compiler is MSVC++ 17 Visual studio version 15.3 with compiler option /std:c++14 (as opposed to /std:c++latest) running in release mode:

struct Bar
{
    int a;
    std::string b;
    Bar() { std::cout << "default\n";  }
    Bar(int a, const std::string& b)    : a{ a }, b{ b } { std::cout << "direct\n"; }
    Bar(int a, std::string&& b)         : a{ a }, b{ std::move(b) } { std::cout << "direct move b\n"; }
    Bar(const Bar& other)               : a{ other.a }, b{ other.b } { std::cout << "const copy\n"; }
    Bar(Bar&& other)                    : a{ std::move(other.a) }, b{ std::move(other.b) } { std::cout << "move\n"; }
    Bar& operator=(const Bar& other)
    {
        a = other.a;
        b = other.b;
        std::cout << "const assign\n";
        return *this;
    }

    Bar& operator=(Bar&& other)
    {
        a = std::move(other.a); //would this even be correct?
        b = std::move(other.b); 
        std::cout << "move assign\n";
        return *this;
    }
};

std::tuple<Bar, Bar> foo()
{
    std::string s = "dsdf";
    return { { 1, s }, { 5, "asdf" } };
}

int main()
{
    Bar a, b;
    std::tie(a, b) = foo();
    std::cout << a.a << a.b << std::endl;
    std::cout << b.a << b.b;
}

The output is:

default
default
direct
direct move b
const copy <-- Why copy? Why not move>
const copy <-- Why copy? Why not move>
move assign
move assign
1dsdf
5asdf

If I change return { { 1, s }, { 5, "asdf" } }; to return { Bar{ 1, s }, Bar{ 5, "asdf" } }; the output changes to:

default
default
direct
direct move b
move
move
move assign
move assign
1dsdf
5asdf

Question: Why isn't a move performed in both cases? Why is the copy constructor called in the first case?

1 Answers
Related