Hao Shi Hao Shi - 2 months ago 14
C++ Question

Function return a tuple made of vectors

I am trying avoid output arguments in my functions. The old function is:

void getAllBlockMeanError(
const vector<int> &vec, vector<int> &fact, vector<int> &mean, vector<int> &err)


Here
vec
is input argument,
fact
,
mean
and
err
are output argument. I tried to group output argument to one tuple:

tuple< vector<int>, vector<int>, vector<int> >
getAllBlockMeanErrorTuple(const vector<int> &vec)
{
vector<int> fact, mean, err;
//....
return make_tuple(fact, mean, err);
}


Now I can call the new function with:

tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec);


It looks cleaner to me. While I have a question, how does equal assignment of
tie(fact, mean, err)
work? Does it do a deep copy or a move? Since
fact
,
mean
and
err
inside
getAllBlockMeanErrorTuple
will be destroyed, I hope it is doing a move instead of a deep copy.

Answer

You function signature is tuple< vector<int>, vector<int>, vector<int> >, which is a temporary and the elements are eligible to be moved, so

std::tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec)

should move-assign fact, mean, and err.

Here's a sample program for you to see for yourself (demo):

#include <iostream>
#include <vector>
#include <tuple>

struct A
{
    A() = default;
    ~A() = default;
    A(const A&)
    {
        std::cout << "Copy ctor\n";
    }
    A(A&&)
    {
        std::cout << "Move ctor\n";
    }
    A& operator=(const A&)
    {
        std::cout << "Copy assign\n";
        return *this;
    }
    A& operator=(A&&)
    {
        std::cout << "Move assign\n";
        return *this;
    }
};

std::tuple<A, A> DoTheThing()
{
    A first;
    A second;
    return std::make_tuple(first, second);
}

int main()
{
    A first;
    A second;
    std::tie(first, second) = DoTheThing();
}

Output:

Copy ctor
Copy ctor
Move assign
Move assign

Note that the function had to create copies of the vectors for returning the tuple, which may not be what you want. You may want to std::move the elements into std::make_tuple:

return make_tuple(std::move(fact), std::move(mean), std::move(err));

Here's the same example as above, but with std::move used in make_tuple

Note that with C++17's Structured Bindings, you can forget about using std::tie at all, and lean more on auto (Thanks, @Yakk):

auto[fact, mean, err] = getAllBlockMeanErrorTuple(vec);

The early implementations of the C++17 standard for clang (3.8.0) and gcc (6.1.0) don't support it yet, however it seems there is some support in clang 4.0.0: Demo (Thanks, @Revolver_Ocelot)

You'll notice that the output with structured bindings changes to:

Move ctor
Move ctor

Indicating that they take advantage of copy-elision, which saves additional move operations.

Comments