如何从尾部而不是头部拉出可变参数模板参数?

发布于 2024-11-19 05:21:02 字数 925 浏览 2 评论 0原文

由于愚蠢的原因,我不会进入这里,我需要注释掉的行才能工作,而它上面的行则不起作用:

template<uint _N, typename... _Args>
struct PartialTuple;

template<uint _N, typename _Arg, typename... _Args>
struct PartialTuple<_N, _Arg, _Args...>: PartialTuple<_N-1, _Args...> {};

template<typename _Arg, typename... _Args>
struct PartialTuple<0, _Arg, _Args...>
{
    typedef std::tuple<_Arg, _Args...> type;
};

int main()
{
    // I want this to not work...
    PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};

    // I want this to work...
    //PartialTuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}

我尝试将 _Arg_Args...,但这不会编译(至少在 GCC 4.6 中):

error: parameter pack argument ‘_Args ...’ must be at the end of the template argument list

如何从尾部而不是从头部拉出项目?

For silly reasons I'll not go into here, I need the commented out line to work and the line above it it to not work:

template<uint _N, typename... _Args>
struct PartialTuple;

template<uint _N, typename _Arg, typename... _Args>
struct PartialTuple<_N, _Arg, _Args...>: PartialTuple<_N-1, _Args...> {};

template<typename _Arg, typename... _Args>
struct PartialTuple<0, _Arg, _Args...>
{
    typedef std::tuple<_Arg, _Args...> type;
};

int main()
{
    // I want this to not work...
    PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};

    // I want this to work...
    //PartialTuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}

I tried swapping _Arg with _Args..., but that won't compile (at least in GCC 4.6):

error: parameter pack argument ‘_Args ...’ must be at the end of the template argument list

How can I pull items off from the tail instead of from the head?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

爱冒险 2024-11-26 05:21:02

这是一个解决方案:我不从后面截断 N,而是从前面截断 sizeof...(Args) - N

#include <tuple>

/* Concatenator helper */

template <typename T, typename Tuple> struct cat;
template <typename T, typename ...Args>
struct cat<T, std::tuple<Args...>>
{
  typedef typename std::tuple<T, Args...> value;
};


/* Head-of-tuple */

template <unsigned int, typename...> struct tuple_head;

// Base case. Need to specialize twice, once for one and once for variadic types
template <typename ...Args>
struct tuple_head<0, Args...>
{
  typedef std::tuple<> value;
};
template <typename T>
struct tuple_head<0, T>
{
  typedef std::tuple<> value;
};

// Recursion step
template <unsigned int N, typename T, typename ...Args>
struct tuple_head<N, T, Args...>
{
  typedef typename cat<T, typename tuple_head<N - 1, Args...>::value>::value value;
};


/* User interface */

template <unsigned int N, typename ...Args>
struct PartialTuple
{
  typedef typename tuple_head<sizeof...(Args) - N, Args...>::value type;
};


/* Usage */

#include <string>
int main()
{
  // I want this to not work...
  //PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};

  // I want this to work...
  PartialTuple<1, std::string, std::string, int, int>::type B("test", "test", 5);
  PartialTuple<0, std::string, std::string, int, int>::type C("test", "test", 5, 6);
}

Here's a solution: Instead of truncating N from the back, I just truncate sizeof...(Args) - N from the front:

#include <tuple>

/* Concatenator helper */

template <typename T, typename Tuple> struct cat;
template <typename T, typename ...Args>
struct cat<T, std::tuple<Args...>>
{
  typedef typename std::tuple<T, Args...> value;
};


/* Head-of-tuple */

template <unsigned int, typename...> struct tuple_head;

// Base case. Need to specialize twice, once for one and once for variadic types
template <typename ...Args>
struct tuple_head<0, Args...>
{
  typedef std::tuple<> value;
};
template <typename T>
struct tuple_head<0, T>
{
  typedef std::tuple<> value;
};

// Recursion step
template <unsigned int N, typename T, typename ...Args>
struct tuple_head<N, T, Args...>
{
  typedef typename cat<T, typename tuple_head<N - 1, Args...>::value>::value value;
};


/* User interface */

template <unsigned int N, typename ...Args>
struct PartialTuple
{
  typedef typename tuple_head<sizeof...(Args) - N, Args...>::value type;
};


/* Usage */

#include <string>
int main()
{
  // I want this to not work...
  //PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};

  // I want this to work...
  PartialTuple<1, std::string, std::string, int, int>::type B("test", "test", 5);
  PartialTuple<0, std::string, std::string, int, int>::type C("test", "test", 5, 6);
}
燕归巢 2024-11-26 05:21:02

我整晚都在玩它,终于找到了一些东西(改变了我的外壳以匹配 STL):

template<uint _N, typename... _All>
struct reverse_tuple_outer
{
    template<typename _Head, typename... _Tail>
    struct reverse_tuple_inner: reverse_tuple_outer<_N-1, _Head, _All...>::template reverse_tuple_inner<_Tail...> { };
};

template<typename... _All>
struct reverse_tuple_outer<0, _All...>
{
    template<typename... _Tail>
    struct reverse_tuple_inner {
        typedef std::tuple<_All...> type;
    };
};

template<typename... _Args>
struct reverse_tuple
{
    typedef typename reverse_tuple_outer<sizeof...(_Args)>::template reverse_tuple_inner<_Args...>::type type;
};

template<typename... _Args>
struct strip_and_reverse_tuple;

template<typename... _Args>
struct strip_and_reverse_tuple<std::tuple<_Args...>>
{
    typedef typename reverse_tuple<_Args...>::type type;
};

template<uint _N, typename... _Args>
struct partial_tuple
{
    typedef typename strip_and_reverse_tuple<typename reverse_tuple_outer<sizeof...(_Args)-_N>::template reverse_tuple_inner<_Args...>::type>::type type;
};

int main()
{
    //partial_tuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
    partial_tuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}

作为额外的好处,我还有 reverse_tuple,如果我需要它的话。

I've been playing with it all night and finally got something to work (changed my casing to match the STL):

template<uint _N, typename... _All>
struct reverse_tuple_outer
{
    template<typename _Head, typename... _Tail>
    struct reverse_tuple_inner: reverse_tuple_outer<_N-1, _Head, _All...>::template reverse_tuple_inner<_Tail...> { };
};

template<typename... _All>
struct reverse_tuple_outer<0, _All...>
{
    template<typename... _Tail>
    struct reverse_tuple_inner {
        typedef std::tuple<_All...> type;
    };
};

template<typename... _Args>
struct reverse_tuple
{
    typedef typename reverse_tuple_outer<sizeof...(_Args)>::template reverse_tuple_inner<_Args...>::type type;
};

template<typename... _Args>
struct strip_and_reverse_tuple;

template<typename... _Args>
struct strip_and_reverse_tuple<std::tuple<_Args...>>
{
    typedef typename reverse_tuple<_Args...>::type type;
};

template<uint _N, typename... _Args>
struct partial_tuple
{
    typedef typename strip_and_reverse_tuple<typename reverse_tuple_outer<sizeof...(_Args)-_N>::template reverse_tuple_inner<_Args...>::type>::type type;
};

int main()
{
    //partial_tuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
    partial_tuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}

As an added bonus, I also have reverse_tuple, should I ever need it.

Bonjour°[大白 2024-11-26 05:21:02

我让我的代码有点像 Haskell 中的列表 - 因为,TMP 是 C++ 中的纯函数式语言。

add_to_pack 相当于 Haskell 的列表构造函数 (:)drop_from_end 实现为(用 Haskell 表示法)\x list -> take (length list - x) list,其中 take n 仅获取列表的前 n 个元素。

我想您可以直接使用 std::tuple 而不是 pack,但我更喜欢这个解决方案,因为它不会滥用 tuple 作为模板参数包持有者。 :)

这是代码:

#include <tuple>
#include <type_traits> // for std::conditional


template <typename... Pack>
struct pack
{ };


template <typename, typename>
struct add_to_pack;

template <typename A, typename... R>
struct add_to_pack<A, pack<R...>>
{
  typedef pack<A, R...> type;
};


template <typename>
struct convert_to_tuple;

template <typename... A>
struct convert_to_tuple<pack<A...>>
{
  typedef std::tuple<A...> type;
};


template <int, typename...>
struct take;

template <int N>
struct take<N>
{
  typedef pack<> type;
};

template <int N, typename Head, typename... Tail>
struct take<N, Head, Tail...>
{
  typedef
    typename std::conditional<
      (N > 0),
      typename add_to_pack<
        Head,
        typename take<
          N - 1,
          Tail...
        >::type
      >::type,
      pack<>
    >::type type;
};  


template <int N, typename... A>
struct drop_from_end
{
  // Add these asserts if needed.
  //static_assert(N >= 0,
  //  "Cannot drop negative number of elements!");

  //static_assert(N <= static_cast<int>(sizeof...(A)),
  //  "Cannot drop more elements than size of pack!")

  typedef
    typename convert_to_tuple<
      typename take<
        static_cast<int>(sizeof...(A)) - N,
        A...
      >::type
    >::type type;
};


int main()
{
  drop_from_end<2, const char*, double, int, int>::type b{"pi", 3.1415};
}

这是工作中的代码:来自 ideone.com


take 结构或多或少相当于以下 Haskell 代码:

take n []     = []
take n (x:xs)
  | n > 0     = x : take (n - 1) xs
  | otherwise = []

I've made my code work a little bit like lists in Haskell - because, well, TMP is purely functional language inside C++.

add_to_pack is equivalent to Haskell's list constructor (:). drop_from_end is implemented as (in Haskell notation) \x list -> take (length list - x) list, where take n just takes first n elements of the list.

I suppose you could use std::tuple directly instead of pack, but I liked this solution better, because it doesn't misuse tuple as template parameter pack holder. :)

Here's the code:

#include <tuple>
#include <type_traits> // for std::conditional


template <typename... Pack>
struct pack
{ };


template <typename, typename>
struct add_to_pack;

template <typename A, typename... R>
struct add_to_pack<A, pack<R...>>
{
  typedef pack<A, R...> type;
};


template <typename>
struct convert_to_tuple;

template <typename... A>
struct convert_to_tuple<pack<A...>>
{
  typedef std::tuple<A...> type;
};


template <int, typename...>
struct take;

template <int N>
struct take<N>
{
  typedef pack<> type;
};

template <int N, typename Head, typename... Tail>
struct take<N, Head, Tail...>
{
  typedef
    typename std::conditional<
      (N > 0),
      typename add_to_pack<
        Head,
        typename take<
          N - 1,
          Tail...
        >::type
      >::type,
      pack<>
    >::type type;
};  


template <int N, typename... A>
struct drop_from_end
{
  // Add these asserts if needed.
  //static_assert(N >= 0,
  //  "Cannot drop negative number of elements!");

  //static_assert(N <= static_cast<int>(sizeof...(A)),
  //  "Cannot drop more elements than size of pack!")

  typedef
    typename convert_to_tuple<
      typename take<
        static_cast<int>(sizeof...(A)) - N,
        A...
      >::type
    >::type type;
};


int main()
{
  drop_from_end<2, const char*, double, int, int>::type b{"pi", 3.1415};
}

And here's the code at work: via ideone.com.


The take struct is more or less equivalent to following Haskell code:

take n []     = []
take n (x:xs)
  | n > 0     = x : take (n - 1) xs
  | otherwise = []
稳稳的幸福 2024-11-26 05:21:02

我使用 Boost.MPL 和 Boost.Fusion 做了类似的事情:使用 MPL 工具(例如 push_back)计算类型序列,然后将其转换为 fusion::vector fusion::as_vector 和 MPL 适配器。不过,我已经有一个助手将 fusion::vector 转换为 std::tuple 。

I have done something similar using Boost.MPL and Boost.Fusion: compute the type sequence using the MPL facilities such as push_back, then convert it to a fusion::vector with fusion::as_vector and MPL adaptors. I already had a helper to convert a fusion::vector to std::tuple though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文