Hao Shi
Hao Shi

Reputation: 523

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.

Upvotes: 13

Views: 3465

Answers (2)

AndyG
AndyG

Reputation: 41100

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.

Upvotes: 17

Jarod42
Jarod42

Reputation: 217293

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

would do a move assignment.

But as mentioned in comment

return make_tuple(fact, mean, err);

would do a copy, you may solve that with:

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

Upvotes: 12

Related Questions