The following paper is the first proposal I found for template parameter packs.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1603.pdf
At page 16, it talks about introducing two new operators [] and <> for accessing parameter pack elements and parameter pack types.
The suggested syntax for such an operator involves two new operators: .[] to access values and .<> to access types. For instance:
template<int N, typename Tuple> struct tuple_element;
template<int N, ... Elements>
struct tuple_element<tuple<Elements...> >
{
typedef Elements.<N> type;
};
template<int N, ... Elements>
Elements.<N>& get(tuple<Elements...>& t)
{ return t.[N]; }
template<int N, ... Elements>
const Elements.<N>& get(const tuple<Elements...>& t)
{ return t.[N]; }
So where are these operators? If there is none, what is their replacement?
Others have already answered that it can be done via std::tuple
. If you want to access the Nth type of a parameter pack, you may find the following metafunction handy:
template<int N, typename... Ts> using NthTypeOf =
typename std::tuple_element<N, std::tuple<Ts...>>::type;
Usage:
using ThirdType = NthTypeOf<2, Ts...>;
C++11 doesn't have corresponding operators which is the reason they are proposed. With C++11 you'll need to either extract the corresponding information yourself or use a class which already does the necessary operation. The easiest approach is probably to just use std::tuple<T...>
which already implements the corresponding logic.
If you wonder how std::tuple<T...>
currently implements these operations: it is basically an exercise in functional programming using a fairly bad functional programming notation. Once you know how to get the n
-th type of the sequence, getting the n
-th element using inheritance from base classes parameterized on index and type is fairly trivial. Implementing something like tuple_element<N, T...>
could look something like this:
template <int N, typename... T>
struct tuple_element;
template <typename T0, typename... T>
struct tuple_element<0, T0, T...> {
typedef T0 type;
};
template <int N, typename T0, typename... T>
struct tuple_element<N, T0, T...> {
typedef typename tuple_element<N-1, T...>::type type;
};
The actual more challenging bit in implementing something like std::tuple<T...>
is conjuring up a list of indices so you got a parallel list of type and integers which can then be expanded, e.g., for a list of base classes using something like (how the internal details look exactly will differ but the basic idea of having a parallel parameters packs for the types and their indices will be somehow there):
template <typename... T, int... I>
class tuple_base<tuple_types<T...>, tuple_indices<I...>>:
public tuple_field<T, I>... {
};
Access N-th element?
Using std::forward_as_tuple
:
template <int I, class... Ts>
decltype(auto) get(Ts&&... ts) {
return std::get<I>(std::forward_as_tuple(ts...));
}
Example usage:
template<class...Ts>
void foo(Ts&&...ts){
auto& first = get<0>(ts...);
auto second = get<1>(ts...);
first = 'H';
second = 'E';
(std::cout << ... << ts);
}
foo('h','e','l','l','o');
// prints "Hello"
This answer is to supplement Emile Cormier's answer which gives only the n-th type.
Using tuple_element for getting the return type for the Nth element:
template<size_t index, typename T, typename... Ts>
inline constexpr typename enable_if<index==0, T>::type
get(T&& t, Ts&&... ts) {
return t;
}
template<size_t index, typename T, typename... Ts>
inline constexpr typename enable_if<(index > 0) && index <= sizeof...(Ts),
typename tuple_element<index, tuple<T, Ts...>>::type>::type
get(T&& t, Ts&&... ts) {
return get<index-1>(std::forward<Ts>(ts)...);
}
// below is optional - just for getting a more readable compilation error
// in case calling get with a bad index
inline template<long long index, typename... Ts>
constexpr bool index_ok() {
return index >= 0 && index < sizeof...(Ts);
}
template<long long index, typename T, typename... Ts>
inline constexpr
typename enable_if<!index_ok<index, T, Ts...>(), T>::type
get(T&& t, Ts&&... ts) {
static_assert(index_ok<index, T, Ts...>(),
"bad index in call to get, smaller than zero or above pack size");
return t;
}
Without using tuple, relying on auto return type and specifically on C++14 decltype(auto) and on using enable_if as a template parameter and not as a return type:
template<size_t index, typename T, typename... Ts,
typename enable_if<index==0>::type* = nullptr>
inline constexpr decltype(auto) get(T&& t, Ts&&... ts) {
return std::forward<T>(t);
}
template<size_t index, typename T, typename... Ts,
typename enable_if<(index > 0 && index <= sizeof...(Ts))>::type* = nullptr>
inline constexpr decltype(auto) get(T&& t, Ts&&... ts) {
return get<index-1>(std::forward<Ts>(ts)...);
}
template<long long index, typename... Ts>
inline constexpr bool index_ok() {
return index >= 0 && index < (long long)sizeof...(Ts);
}
// block (compilation error) the call to get with bad index,
// providing a readable compilation error
template<long long index, typename T, typename... Ts,
typename enable_if<(!index_ok<index, T, Ts...>())>::type* = nullptr>
inline constexpr decltype(auto) get(T&& t, Ts&&... ts) {
static_assert(index_ok<index, T, Ts...>(),
"bad index in call to get, smaller than zero or above pack size");
return std::forward<T>(t); // need to return something...
// we hope to fail on the static_assert above
}
template<size_t index, typename... Ts>
void resetElementN(Ts&&... ts) {
get<index>(std::forward<Ts>(ts)...) = {}; // assuming element N has an empty ctor
}
int main() {
int i = 0;
string s = "hello";
get<0>(i,2,"hello","hello"s, 'a') += get<0>(2);
get<1>(1,i,"hello",4) += get<1>(1, 2);
get<3>(1,2,"hello",i) += get<2>(0, 1, 2);
get<2>(1,2,s,4) = get<2>(0, 1, "hi");
cout << i << ' ' << s << endl;
resetElementN<1>(0, i, 2);
resetElementN<0>(s, 1, 2);
cout << i << ' ' << s << endl;
// not ok - and do not compile
// get<0>(1,i,"hello","hello"s) = 5;
// get<1>(1,i*2,"hello") = 5;
// get<2>(1,i*2,"hello")[4] = '!';
// resetElementN<1>(s, 1, 2);
// ok
const int j = 2;
cout << get<0>(j,i,3,4) << endl;
// not ok - and do not compile
// get<0>(j,i,3,4) = 5;
// not ok - and do not compile
// with a readable compilation error
// cout << get<-1>("one", 2, '3') << endl;
// cout << get<3>("one", 2, '3') << endl;
}
Code
Option 1: http://coliru.stacked-crooked.com/a/60ad3d860aa94453
Option 2: http://coliru.stacked-crooked.com/a/09f6e8e155612f8b
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With