Reputation: 653
I frequently write snippets like
int x,y,z; tie(x,y,z) = g[19];
where, for instance, g
was declared earlier
vector<tuple<int,int,int>> g(100);
Problem is, maybe later I actually want x
and y
to point to the internals of g
by reference, and the refactoring is ugly, e.g.
int &x = get<0>(g[19]);
int &y = get<1>(g[19]);
int &z = get<2>(g[19]);
or sometimes even worse, for instance if the access is a more complex expression
tuple<int,int,int> &p = g[19]; // if the rhs was actually more complicated
int &x = get<0>(p);
int &y = get<1>(p);
int &z = get<2>(p);
Is there a better refactoring, more in the style of the assignment to tie(..)?
The difficulty as I understand it is that references insist on being initialized exactly at their declaration. So, in possibly other words, is there a way to use tie
-like syntax for multiple variable initialization in c++ (this would also make the earlier non reference usage cleaner)?
Upvotes: 55
Views: 40259
Reputation: 3321
Fortunately, C++17 has a solution for exactly this problem, the structured binding declaration. Even the non-reference interface can be improved.
auto[x, y, z] = g[i];
The above line declares x, y,z and initializes them with the values of g[i]
. Not only is it cleaner, but it could be more efficient for types that are expensive to construct.
To get references to the members of g[i]
, one can write
auto& [x, y, z] = g[i];
Upvotes: 79
Reputation: 2837
You can automate it with a function so that you don't have to type out 3 (or more) lines:
template <class... Ts, std::size_t... Is, class Tuple>
decltype( auto ) tie_from_specified( std::index_sequence<Is...>, Tuple& tuple )
{
return std::tuple<Ts...>{ std::get<Is>( tuple )... };
}
template <class... Ts, class Tuple>
decltype( auto ) tie_from( Tuple& tuple )
{
return tie_from_specified<Ts...>( std::make_index_sequence<sizeof...( Ts )>{}, tuple );
}
Usage would be:
int x{ 2 };
std::tuple<int, int&, int> g19( 1, x, 3 );
// new tuple: ref to get<0>( g19 ), value of get<1>( g19 ), ref to get<2>( g19 )
auto t0{ tie_from<int&, int, int&>( g19 ) };
// new tuple: ref to get<0>( g19 ), ref to get<2>( g19 )
auto t1{ tie_from_specified<int&, int&>( std::index_sequence<0, 2>{}, g19 ) };
Upvotes: 8
Reputation: 1261
You could write a struct to store the references:
struct vector3d
{
vector3d(std::tuple<int, int, int>& tuple)
: x(std::get<0>(tuple)),
y(std::get<1>(tuple)),
z(std::get<2>(tuple))
{}
int& x;
int& y;
int& z;
};
And then use it like this:
vector3d vec(g[6]);
// do sth with vec.x or vec.y or vec.z or all together :)
Upvotes: 0