Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return std::tuple and move semantics / copy elision

I have the following factory function:

auto factory() -> std::tuple<bool, std::vector<int>>
{
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);

    return { true, vec };
}

auto [b, vec] = factory();

In the return statement is vec considered an xvalue or prvalue and therefore moved or copy elided?

My guess is no, because the compiler, when list-initializing the std::tuple in the return statement, still doesn't know that vec is going to be destroyed. So maybe an explicit std::move is required:

auto factory() -> std::tuple<bool, std::vector<int>>
{
    ...
    return { true, std::move(vec) };
}

auto [b, vec] = factory();

Is it that really required?

like image 410
Tarquiscani Avatar asked Jul 25 '18 14:07

Tarquiscani


1 Answers

In the return statement is vec considered an xvalue or prvalue and therefore moved or copy elided?

vec is always an lvalue. Even in the simple case:

std::vector<int> factory() {
    std::vector<int> vec;
    return vec;
}

That is still returning an lvalue. It's just that we have special rules that say that we just ignore the copy in this case when we're returning the name of an automatic object (and another special rule in the case that copy elision doesn't apply, but we still try to move from lvalues).

But those special rules only apply to the return object; case, they don't apply to the return {1, object}; case, no matter how similar it might look. In your code here, that would do a copy, because that's what you asked for. If you want to do a move, you must do:

return {1, std::move(object)};

And in order to avoid the move, you must do:

auto factory() -> std::tuple<bool, std::vector<int>>
{
    std::tuple<bool, std::vector<int>> t;

    auto& [b, vec] = t;
    b = true;
    vec.push_back(1);
    vec.push_back(2);
    return t;
}
like image 60
Barry Avatar answered Oct 22 '22 03:10

Barry