Reputation: 523
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
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
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