I want an utility to transform a std::tuple<T,U>
to std::pair<T,U>
, while leaving a std::tuple<T, U, V, W...>
unchanged.
Furthermore, I want this utility to
std::tuple
;The last point is the difficult part, for me, and it's the reason I'm asking this question.
I know that tuples can store values as well as references (lvalue references as well as rvalue references), so imagine that the sought function, if fed with an rvalue 2-tuple, it could steal resources from the non-reference components, but should leave intact the reference component.
For instance given
A a;
B b;
std::tuple<A, B&> t{a,b};
where I know a
is copied in the tuple, while b
is not, I think that doing
auto p{to_pair(t)};
should result in a std::pair<A, B&>
which holds another copy of a
and a reference to the only b
that exists so far.
On the other hand, doing
B b;
auto p{to_pair(std::tuple<A, B&>{A{},b})};
should result again in a std::pair<A, B&>
, but this would hold the very A{}
of which no copy should ever be made; and it would hold a reference to b
anyway.
The scenario described above is already enough for me to be in doubt about what to do, let alone thinking of other combinations of &
/const&
/&&
.
Some time ago I had a hard time answering a question about std::tuple
s and perfect forwarding, but I can never say I've understood them fully.
template<class...Fs>
struct overloaded : Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs&&...)->overloaded<std::decay_t<Fs>...>;
auto move_tuples = []<class...Ts>(std::tuple<Ts...> x){return std::move(x);};
auto to_pair = []<class A, class B>(std::tuple<A, B> x){
return std::pair<A,B>( std::get<0>(std::move(x)), std::get<1>(std::move(x)) );
};
auto your_function = overloaded{move_tuples, to_pair};
I think that is what you want.
I take the arguments by value, then move them to the return value. If they are values, the result is that the contained values are moved. If they are references, they aren't, the references are just propagated.
The get<N>
"does the right thing" when passed an rvalue tuple. If returns an rvalue from a value.
In c++17 you'll have to replace the lambdas with manual structs.
Or write two normal overloads, and
auto obj=[](auto&&x)->decltype(normal_func(decltype(x)(x))){return decltype(x)(x);};
to turn the overloaded function call normal_func
into a single function object.