模板参数包访问第N个类型和第N个元素

nur*_*tin 37 c++ variadic-templates c++11

以下文章是我为模板参数包找到的第一个提案.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1603.pdf

在第16页,它讨论了引入两个新的运算符[]和<>来访问参数包元素和参数包类型.

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]; }
Run Code Online (Sandbox Code Playgroud)

那么这些运营商在哪里?如果没有,他们的替代品是什么?

Emi*_*ier 44

其他人已经回答说可以通过std::tuple.如果要访问参数包的第N种类型,您可能会发现以下元函数:

template<int N, typename... Ts> using NthTypeOf =
        typename std::tuple_element<N, std::tuple<Ts...>>::type;
Run Code Online (Sandbox Code Playgroud)

用法:

using ThirdType = NthTypeOf<2, Ts...>;
Run Code Online (Sandbox Code Playgroud)


Die*_*ühl 24

C++ 11没有相应的运算符,这就是它们被提出的原因.使用C++ 11,您需要自己提取相应的信息或使用已经执行必要操作的类.最简单的方法可能就是使用std::tuple<T...>已实现相应逻辑的方法.

如果您想知道std::tuple<T...>当前如何实现这些操作:它基本上是使用相当糟糕的函数式编程符号进行函数式编程的练习.一旦你知道如何获得n序列的-th类型,n使用索引和类型参数化的基类继承获取-th元素是相当简单的.实现类似的东西tuple_element<N, T...>看起来像这样:

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;
};
Run Code Online (Sandbox Code Playgroud)

实现类似的实际更具挑战性的一点std::tuple<T...>就是构建一个索引列表,这样你就可以得到一个类型和整数的并行列表,然后可以扩展它,例如,使用类似内部细节看起来的基类列表会有所不同,但为类型及其索引提供并行参数包的基本思路将以某种方式存在):

template <typename... T, int... I>
class tuple_base<tuple_types<T...>, tuple_indices<I...>>:
     public tuple_field<T, I>... {
};
Run Code Online (Sandbox Code Playgroud)


tom*_*tom 8

访问第N个元素?

使用std::forward_as_tuple:

template <int I, class... Ts>
decltype(auto) get(Ts&&... ts) {
  return std::get<I>(std::forward_as_tuple(ts...));
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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"
Run Code Online (Sandbox Code Playgroud)

这个答案是为了补充Emile Cormier的答案,它只给出了第n种类型.


Ami*_*rsh 5

要从包中获取第N个元素,您可以编写:

选项1

使用tuple_element获取第N个元素的返回类型:

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;
}
Run Code Online (Sandbox Code Playgroud)

选项2

不使用元组,依赖于自动返回类型,特别是在C++ 14上的decltype(auto)和使用enable_if作为模板参数而不是作为返回类型:

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
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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;
}
Run Code Online (Sandbox Code Playgroud)

代码
选项1:http://coliru.stacked-crooked.com/a/60ad3d860aa94453
选项2:http://coliru.stacked-crooked.com/a/09f6e8e155612f8b