What should tuple_map return?
Clash Royale CLAN TAG#URR8PPP
up vote
12
down vote
favorite
I want to implement a generic tuple_map
function that takes a functor and an std::tuple
, applies the functor to every element of this tuple and returns an std::tuple
of results. The implementation is pretty straightforward, however the question arises: what type should this function return? My implementation used std::make_tuple
. However, here std::forward_as_tuple
was suggested.
To be more specific, the implementation (handling of empty tuples is omitted for brevity):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::make_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::forward_as_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Tuple, class Fn>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple)
return tuple_map_v(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
template<class Tuple, class Fn>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple)
return tuple_map_r(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
In the case 1 we use std::make_tuple
which decays type of each argument (_v
for value), and in the case 2 we use std::forward_as_tuple
which preserves references (_r
for reference). Both cases have their pros and cons.
Dangling references.
auto copy = (auto x) return x; ;
auto const_id = (const auto& x) -> decltype(auto) return x; ;
auto r1 = tuple_map_v(copy, std::make_tuple(1));
// OK, type of r1 is std::tuple<int>
auto r2 = tuple_map_r(copy, std::make_tuple(1));
// UB, type of r2 is std::tuple<int&&>
std::tuple<int> r3 = tuple_map_r(copy, std::make_tuple(1));
// Still UB
std::tuple<int> r4 = tuple_map_r(const_id, std::make_tuple(1));
// OK nowTuple of references.
auto id = (auto& x) -> decltype(auto) return x; ;
int a = 0, b = 0;
auto r1 = tuple_map_v(id, std::forward_as_tuple(a, b));
// Type of r1 is std::tuple<int, int>
++std::get<0>(r1);
// Increments a copy, a is still zero
auto r2 = tuple_map_r(id, std::forward_as_tuple(a, b));
// Type of r2 is std::tuple<int&, int&>
++std::get<0>(r2);
// OK, now a = 1Move-only types.
NonCopyable nc;
auto r1 = tuple_map_v(id, std::forward_as_tuple(nc));
// Does not compile without a copy constructor
auto r2 = tuple_map_r(id, std::forward_as_tuple(nc));
// OK, type of r2 is std::tuple<NonCopyable&>References with
std::make_tuple
.auto id_ref = (auto& x) return std::reference_wrapper(x); ;
NonCopyable nc;
auto r1 = tuple_map_v(id_ref, std::forward_as_tuple(nc));
// OK now, type of r1 is std::tuple<NonCopyable&>
auto r2 = tuple_map_v(id_ref, std::forward_as_tuple(a, b));
// OK, type of r2 is std::tuple<int&, int&>
(Probably, I got something wrong or missed something important.)
It seems that make_tuple
is the way to go: it doesn't produce dangling references and still can be forced to deduce a reference type. How would you implement tuple_map
(and what would be the pitfalls associated with it)?
c++ c++17 stdtuple
add a comment |Â
up vote
12
down vote
favorite
I want to implement a generic tuple_map
function that takes a functor and an std::tuple
, applies the functor to every element of this tuple and returns an std::tuple
of results. The implementation is pretty straightforward, however the question arises: what type should this function return? My implementation used std::make_tuple
. However, here std::forward_as_tuple
was suggested.
To be more specific, the implementation (handling of empty tuples is omitted for brevity):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::make_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::forward_as_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Tuple, class Fn>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple)
return tuple_map_v(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
template<class Tuple, class Fn>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple)
return tuple_map_r(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
In the case 1 we use std::make_tuple
which decays type of each argument (_v
for value), and in the case 2 we use std::forward_as_tuple
which preserves references (_r
for reference). Both cases have their pros and cons.
Dangling references.
auto copy = (auto x) return x; ;
auto const_id = (const auto& x) -> decltype(auto) return x; ;
auto r1 = tuple_map_v(copy, std::make_tuple(1));
// OK, type of r1 is std::tuple<int>
auto r2 = tuple_map_r(copy, std::make_tuple(1));
// UB, type of r2 is std::tuple<int&&>
std::tuple<int> r3 = tuple_map_r(copy, std::make_tuple(1));
// Still UB
std::tuple<int> r4 = tuple_map_r(const_id, std::make_tuple(1));
// OK nowTuple of references.
auto id = (auto& x) -> decltype(auto) return x; ;
int a = 0, b = 0;
auto r1 = tuple_map_v(id, std::forward_as_tuple(a, b));
// Type of r1 is std::tuple<int, int>
++std::get<0>(r1);
// Increments a copy, a is still zero
auto r2 = tuple_map_r(id, std::forward_as_tuple(a, b));
// Type of r2 is std::tuple<int&, int&>
++std::get<0>(r2);
// OK, now a = 1Move-only types.
NonCopyable nc;
auto r1 = tuple_map_v(id, std::forward_as_tuple(nc));
// Does not compile without a copy constructor
auto r2 = tuple_map_r(id, std::forward_as_tuple(nc));
// OK, type of r2 is std::tuple<NonCopyable&>References with
std::make_tuple
.auto id_ref = (auto& x) return std::reference_wrapper(x); ;
NonCopyable nc;
auto r1 = tuple_map_v(id_ref, std::forward_as_tuple(nc));
// OK now, type of r1 is std::tuple<NonCopyable&>
auto r2 = tuple_map_v(id_ref, std::forward_as_tuple(a, b));
// OK, type of r2 is std::tuple<int&, int&>
(Probably, I got something wrong or missed something important.)
It seems that make_tuple
is the way to go: it doesn't produce dangling references and still can be forced to deduce a reference type. How would you implement tuple_map
(and what would be the pitfalls associated with it)?
c++ c++17 stdtuple
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we takeauto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go withforward_as_tuple
knowing that (little) caveat
â Rerito
Aug 7 at 13:36
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39
add a comment |Â
up vote
12
down vote
favorite
up vote
12
down vote
favorite
I want to implement a generic tuple_map
function that takes a functor and an std::tuple
, applies the functor to every element of this tuple and returns an std::tuple
of results. The implementation is pretty straightforward, however the question arises: what type should this function return? My implementation used std::make_tuple
. However, here std::forward_as_tuple
was suggested.
To be more specific, the implementation (handling of empty tuples is omitted for brevity):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::make_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::forward_as_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Tuple, class Fn>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple)
return tuple_map_v(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
template<class Tuple, class Fn>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple)
return tuple_map_r(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
In the case 1 we use std::make_tuple
which decays type of each argument (_v
for value), and in the case 2 we use std::forward_as_tuple
which preserves references (_r
for reference). Both cases have their pros and cons.
Dangling references.
auto copy = (auto x) return x; ;
auto const_id = (const auto& x) -> decltype(auto) return x; ;
auto r1 = tuple_map_v(copy, std::make_tuple(1));
// OK, type of r1 is std::tuple<int>
auto r2 = tuple_map_r(copy, std::make_tuple(1));
// UB, type of r2 is std::tuple<int&&>
std::tuple<int> r3 = tuple_map_r(copy, std::make_tuple(1));
// Still UB
std::tuple<int> r4 = tuple_map_r(const_id, std::make_tuple(1));
// OK nowTuple of references.
auto id = (auto& x) -> decltype(auto) return x; ;
int a = 0, b = 0;
auto r1 = tuple_map_v(id, std::forward_as_tuple(a, b));
// Type of r1 is std::tuple<int, int>
++std::get<0>(r1);
// Increments a copy, a is still zero
auto r2 = tuple_map_r(id, std::forward_as_tuple(a, b));
// Type of r2 is std::tuple<int&, int&>
++std::get<0>(r2);
// OK, now a = 1Move-only types.
NonCopyable nc;
auto r1 = tuple_map_v(id, std::forward_as_tuple(nc));
// Does not compile without a copy constructor
auto r2 = tuple_map_r(id, std::forward_as_tuple(nc));
// OK, type of r2 is std::tuple<NonCopyable&>References with
std::make_tuple
.auto id_ref = (auto& x) return std::reference_wrapper(x); ;
NonCopyable nc;
auto r1 = tuple_map_v(id_ref, std::forward_as_tuple(nc));
// OK now, type of r1 is std::tuple<NonCopyable&>
auto r2 = tuple_map_v(id_ref, std::forward_as_tuple(a, b));
// OK, type of r2 is std::tuple<int&, int&>
(Probably, I got something wrong or missed something important.)
It seems that make_tuple
is the way to go: it doesn't produce dangling references and still can be forced to deduce a reference type. How would you implement tuple_map
(and what would be the pitfalls associated with it)?
c++ c++17 stdtuple
I want to implement a generic tuple_map
function that takes a functor and an std::tuple
, applies the functor to every element of this tuple and returns an std::tuple
of results. The implementation is pretty straightforward, however the question arises: what type should this function return? My implementation used std::make_tuple
. However, here std::forward_as_tuple
was suggested.
To be more specific, the implementation (handling of empty tuples is omitted for brevity):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::make_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
return std::forward_as_tuple(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
// ^^^
template<class Tuple, class Fn>
constexpr auto tuple_map_v(Fn fn, Tuple&& tuple)
return tuple_map_v(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
template<class Tuple, class Fn>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple)
return tuple_map_r(fn, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>);
In the case 1 we use std::make_tuple
which decays type of each argument (_v
for value), and in the case 2 we use std::forward_as_tuple
which preserves references (_r
for reference). Both cases have their pros and cons.
Dangling references.
auto copy = (auto x) return x; ;
auto const_id = (const auto& x) -> decltype(auto) return x; ;
auto r1 = tuple_map_v(copy, std::make_tuple(1));
// OK, type of r1 is std::tuple<int>
auto r2 = tuple_map_r(copy, std::make_tuple(1));
// UB, type of r2 is std::tuple<int&&>
std::tuple<int> r3 = tuple_map_r(copy, std::make_tuple(1));
// Still UB
std::tuple<int> r4 = tuple_map_r(const_id, std::make_tuple(1));
// OK nowTuple of references.
auto id = (auto& x) -> decltype(auto) return x; ;
int a = 0, b = 0;
auto r1 = tuple_map_v(id, std::forward_as_tuple(a, b));
// Type of r1 is std::tuple<int, int>
++std::get<0>(r1);
// Increments a copy, a is still zero
auto r2 = tuple_map_r(id, std::forward_as_tuple(a, b));
// Type of r2 is std::tuple<int&, int&>
++std::get<0>(r2);
// OK, now a = 1Move-only types.
NonCopyable nc;
auto r1 = tuple_map_v(id, std::forward_as_tuple(nc));
// Does not compile without a copy constructor
auto r2 = tuple_map_r(id, std::forward_as_tuple(nc));
// OK, type of r2 is std::tuple<NonCopyable&>References with
std::make_tuple
.auto id_ref = (auto& x) return std::reference_wrapper(x); ;
NonCopyable nc;
auto r1 = tuple_map_v(id_ref, std::forward_as_tuple(nc));
// OK now, type of r1 is std::tuple<NonCopyable&>
auto r2 = tuple_map_v(id_ref, std::forward_as_tuple(a, b));
// OK, type of r2 is std::tuple<int&, int&>
(Probably, I got something wrong or missed something important.)
It seems that make_tuple
is the way to go: it doesn't produce dangling references and still can be forced to deduce a reference type. How would you implement tuple_map
(and what would be the pitfalls associated with it)?
c++ c++17 stdtuple
asked Aug 7 at 13:26
Evgeny
1,135822
1,135822
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we takeauto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go withforward_as_tuple
knowing that (little) caveat
â Rerito
Aug 7 at 13:36
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39
add a comment |Â
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we takeauto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go withforward_as_tuple
knowing that (little) caveat
â Rerito
Aug 7 at 13:36
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we take
auto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go with forward_as_tuple
knowing that (little) caveatâ Rerito
Aug 7 at 13:36
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we take
auto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go with forward_as_tuple
knowing that (little) caveatâ Rerito
Aug 7 at 13:36
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
7
down vote
accepted
The problem you highlighted in your question is that using std::forward_as_tuple
on a functor that returns by value will leave you with an rvalue reference in the resulting tuple.
By using make_tuple
you cannot keep lvalue-refs, however by using forward_as_tuple
, you cannot keep plain values. You can instead rely on std::invoke_result
to find out what are the types your result tuple must hold and use the appropriate std::tuple
constructor.
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
using tuple_type = std::tuple<
typename std::invoke_result<
Fn, decltype(std::get<indices>(std::forward<Tuple>(tuple)))
>::type...
>;
return tuple_type(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
This way you preserve the value category of the result of the fn
call.
Live demo on Coliru
Am I correct that with template argument deduction we don't needtuple_type
and can simplyreturn std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent toforward_as_tuple
.
â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
@Evgeny after some testing, it appears that the deduction guides forstd::tuple
decay their arguments... So explicit type is still the only way to go it seems!
â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment thatstd::tuple(...)
doesn't really work. :)
â Evgeny
Aug 7 at 14:38
4
@Evgenytuple(x...)
is equivalent tomake_tuple(x...)
, with the exception thatreference_wrapper<T>
stays asreference_wrapper<T>
and doesn't becomeT&
.
â Barry
Aug 7 at 15:07
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
7
down vote
accepted
The problem you highlighted in your question is that using std::forward_as_tuple
on a functor that returns by value will leave you with an rvalue reference in the resulting tuple.
By using make_tuple
you cannot keep lvalue-refs, however by using forward_as_tuple
, you cannot keep plain values. You can instead rely on std::invoke_result
to find out what are the types your result tuple must hold and use the appropriate std::tuple
constructor.
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
using tuple_type = std::tuple<
typename std::invoke_result<
Fn, decltype(std::get<indices>(std::forward<Tuple>(tuple)))
>::type...
>;
return tuple_type(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
This way you preserve the value category of the result of the fn
call.
Live demo on Coliru
Am I correct that with template argument deduction we don't needtuple_type
and can simplyreturn std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent toforward_as_tuple
.
â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
@Evgeny after some testing, it appears that the deduction guides forstd::tuple
decay their arguments... So explicit type is still the only way to go it seems!
â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment thatstd::tuple(...)
doesn't really work. :)
â Evgeny
Aug 7 at 14:38
4
@Evgenytuple(x...)
is equivalent tomake_tuple(x...)
, with the exception thatreference_wrapper<T>
stays asreference_wrapper<T>
and doesn't becomeT&
.
â Barry
Aug 7 at 15:07
add a comment |Â
up vote
7
down vote
accepted
The problem you highlighted in your question is that using std::forward_as_tuple
on a functor that returns by value will leave you with an rvalue reference in the resulting tuple.
By using make_tuple
you cannot keep lvalue-refs, however by using forward_as_tuple
, you cannot keep plain values. You can instead rely on std::invoke_result
to find out what are the types your result tuple must hold and use the appropriate std::tuple
constructor.
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
using tuple_type = std::tuple<
typename std::invoke_result<
Fn, decltype(std::get<indices>(std::forward<Tuple>(tuple)))
>::type...
>;
return tuple_type(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
This way you preserve the value category of the result of the fn
call.
Live demo on Coliru
Am I correct that with template argument deduction we don't needtuple_type
and can simplyreturn std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent toforward_as_tuple
.
â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
@Evgeny after some testing, it appears that the deduction guides forstd::tuple
decay their arguments... So explicit type is still the only way to go it seems!
â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment thatstd::tuple(...)
doesn't really work. :)
â Evgeny
Aug 7 at 14:38
4
@Evgenytuple(x...)
is equivalent tomake_tuple(x...)
, with the exception thatreference_wrapper<T>
stays asreference_wrapper<T>
and doesn't becomeT&
.
â Barry
Aug 7 at 15:07
add a comment |Â
up vote
7
down vote
accepted
up vote
7
down vote
accepted
The problem you highlighted in your question is that using std::forward_as_tuple
on a functor that returns by value will leave you with an rvalue reference in the resulting tuple.
By using make_tuple
you cannot keep lvalue-refs, however by using forward_as_tuple
, you cannot keep plain values. You can instead rely on std::invoke_result
to find out what are the types your result tuple must hold and use the appropriate std::tuple
constructor.
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
using tuple_type = std::tuple<
typename std::invoke_result<
Fn, decltype(std::get<indices>(std::forward<Tuple>(tuple)))
>::type...
>;
return tuple_type(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
This way you preserve the value category of the result of the fn
call.
Live demo on Coliru
The problem you highlighted in your question is that using std::forward_as_tuple
on a functor that returns by value will leave you with an rvalue reference in the resulting tuple.
By using make_tuple
you cannot keep lvalue-refs, however by using forward_as_tuple
, you cannot keep plain values. You can instead rely on std::invoke_result
to find out what are the types your result tuple must hold and use the appropriate std::tuple
constructor.
template<class Fn, class Tuple, std::size_t... indices>
constexpr auto tuple_map_r(Fn fn, Tuple&& tuple, std::index_sequence<indices...>)
using tuple_type = std::tuple<
typename std::invoke_result<
Fn, decltype(std::get<indices>(std::forward<Tuple>(tuple)))
>::type...
>;
return tuple_type(fn(std::get<indices>(std::forward<Tuple>(tuple)))...);
This way you preserve the value category of the result of the fn
call.
Live demo on Coliru
answered Aug 7 at 14:07
Rerito
4,6081241
4,6081241
Am I correct that with template argument deduction we don't needtuple_type
and can simplyreturn std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent toforward_as_tuple
.
â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
@Evgeny after some testing, it appears that the deduction guides forstd::tuple
decay their arguments... So explicit type is still the only way to go it seems!
â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment thatstd::tuple(...)
doesn't really work. :)
â Evgeny
Aug 7 at 14:38
4
@Evgenytuple(x...)
is equivalent tomake_tuple(x...)
, with the exception thatreference_wrapper<T>
stays asreference_wrapper<T>
and doesn't becomeT&
.
â Barry
Aug 7 at 15:07
add a comment |Â
Am I correct that with template argument deduction we don't needtuple_type
and can simplyreturn std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent toforward_as_tuple
.
â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
@Evgeny after some testing, it appears that the deduction guides forstd::tuple
decay their arguments... So explicit type is still the only way to go it seems!
â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment thatstd::tuple(...)
doesn't really work. :)
â Evgeny
Aug 7 at 14:38
4
@Evgenytuple(x...)
is equivalent tomake_tuple(x...)
, with the exception thatreference_wrapper<T>
stays asreference_wrapper<T>
and doesn't becomeT&
.
â Barry
Aug 7 at 15:07
Am I correct that with template argument deduction we don't need
tuple_type
and can simply return std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent to forward_as_tuple
.â Evgeny
Aug 7 at 14:26
Am I correct that with template argument deduction we don't need
tuple_type
and can simply return std::tuple(fn(...)...)
? I even wanted to include this alternative into my question, but (erroneously) thought it was equivalent to forward_as_tuple
.â Evgeny
Aug 7 at 14:26
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
@Evgeny Correct. With template deduction guides it would work I think (I am not too confident since I am not familiar with them yet though...).
â Rerito
Aug 7 at 14:28
1
1
@Evgeny after some testing, it appears that the deduction guides for
std::tuple
decay their arguments... So explicit type is still the only way to go it seems!â Rerito
Aug 7 at 14:33
@Evgeny after some testing, it appears that the deduction guides for
std::tuple
decay their arguments... So explicit type is still the only way to go it seems!â Rerito
Aug 7 at 14:33
I've just made a similar test and was going to write a comment that
std::tuple(...)
doesn't really work. :)â Evgeny
Aug 7 at 14:38
I've just made a similar test and was going to write a comment that
std::tuple(...)
doesn't really work. :)â Evgeny
Aug 7 at 14:38
4
4
@Evgeny
tuple(x...)
is equivalent to make_tuple(x...)
, with the exception that reference_wrapper<T>
stays as reference_wrapper<T>
and doesn't become T&
.â Barry
Aug 7 at 15:07
@Evgeny
tuple(x...)
is equivalent to make_tuple(x...)
, with the exception that reference_wrapper<T>
stays as reference_wrapper<T>
and doesn't become T&
.â Barry
Aug 7 at 15:07
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51727961%2fwhat-should-tuple-map-return%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Interesting question. But shouldn't it be the responsibility of the person writing the call to make sure the behavior is well defined? For instance, if we take
auto fn = (auto const& x) return std::cref(x); ; auto a = fn(2);
We would get a dangling reference, without any tuple involved. My conclusion would be to go withforward_as_tuple
knowing that (little) caveatâ Rerito
Aug 7 at 13:36
Did You think of just making separate overloads/SFINAE versions for lvalues and rvalues? I think this would allow You to get the best of both worlds
â bartop
Aug 7 at 13:39