C++如何基于部分特化生成std :: tuple类型?

炸鱼薯*_*德里克 5 c++ templates template-specialization template-meta-programming c++11

我有一个带有整数参数的模板,但基本模板是这样禁用的static_assert().(我只想要一些特定的表格;我希望除了某些参数外,任何传递给模板的参数都是禁止的)

template<ItemID item_id> struct ItemTemplate{
    static_assert(item_id == -1,"Cann't use unspecialized ItemTemplate!");
    static ItemID id{ std::numeric_limits<ItemID>::max() };
    //...
};
Run Code Online (Sandbox Code Playgroud)

我也有这个模板的几个专业化表格(我经常添加或删除其中的一些)

template<> struct ItemTemplate<1>{
    static constexpr ItemID id{1};
    //..
};
template<> struct ItemTemplate<2>{
    static constexpr ItemID id{2};
    //...
};
Run Code Online (Sandbox Code Playgroud)

现在我想创建一个std::tuple仅由所有可用类型初始化的.所以在上面的例子中,ItemTemplate<1>ItemTemplate<2>,而不是ItemTemplate<3>和其他非专业类型.我该如何实现这一目标?

max*_*x66 2

我看到了一种方法,但前提是您忘记了static_assert()拒绝方式并定义ItemTemplate.

以下是一个简化的示例,其中我仅定义了一些特殊化foo,并且foo通用结构保持未定义。

template <std::size_t>
struct foo;

template <> struct foo<2U> {};
template <> struct foo<3U> {};
template <> struct foo<5U> {};
template <> struct foo<7U> {};
Run Code Online (Sandbox Code Playgroud)

现在您需要一些东西来检测类型是否已定义;例如,以下

template <typename T, std::size_t = sizeof(T)>
std::true_type existH (int);

template <typename>
std::false_type existH (long);

template <typename T>
using exist = decltype(existH<T>(0));
Run Code Online (Sandbox Code Playgroud)

即:从exist<foo<0>>::value你得到false和从exist<foo<2>>::value你得到true

foo现在您需要一个从下限(例如零)到上限定义的专业化索引列表(可用的编译时间) 。

您可以通过以下方式获取它

template <std::size_t I, std::size_t topI, typename,
          bool = (I == topI) || exist<foo<I>>::value>
struct fooIndexList;

template <std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<topI, topI, std::index_sequence<Ixs...>, true>
 { using type = std::index_sequence<Ixs...>; };

template <std::size_t I, std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<I, topI, std::index_sequence<Ixs...>, true>
 { using type = typename fooIndexList<I+1U, topI,
                   std::index_sequence<Ixs..., I>>::type; };

template <std::size_t I, std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<I, topI, std::index_sequence<Ixs...>, false>
 { using type = typename fooIndexList<I+1U, topI,
                   std::index_sequence<Ixs...>>::type; };
Run Code Online (Sandbox Code Playgroud)

使用fooIndexList,获得std::tuple具有所有foo定义(从零到上限)的 a 非常简单:

template <std::size_t ... Idx>
constexpr auto makeFooTupleH (std::index_sequence<Idx...> const &)
 { return std::make_tuple( foo<Idx>{} ... ); }

constexpr auto makeFooTuple ()
 { return makeFooTupleH(
      typename fooIndexList<0U, 100U, std::index_sequence<>>::type {}); }
Run Code Online (Sandbox Code Playgroud)

在示例中,上限为100但可以是 的模板参数makeFooTuple()

以下是完整的编译示例

#include <tuple>
#include <utility>
#include <iostream>
#include <type_traits>

template <typename T, std::size_t = sizeof(T)>
std::true_type existH (int);

template <typename>
std::false_type existH (long);

template <typename T>
using exist = decltype(existH<T>(0));

template <std::size_t>
struct foo;

template <> struct foo<2U> {};
template <> struct foo<3U> {};
template <> struct foo<5U> {};
template <> struct foo<7U> {};

template <std::size_t I, std::size_t topI, typename,
          bool = (I == topI) || exist<foo<I>>::value>
struct fooIndexList;

template <std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<topI, topI, std::index_sequence<Ixs...>, true>
 { using type = std::index_sequence<Ixs...>; };

template <std::size_t I, std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<I, topI, std::index_sequence<Ixs...>, true>
 { using type = typename fooIndexList<I+1U, topI,
                   std::index_sequence<Ixs..., I>>::type; };

template <std::size_t I, std::size_t topI, std::size_t ... Ixs>
struct fooIndexList<I, topI, std::index_sequence<Ixs...>, false>
 { using type = typename fooIndexList<I+1U, topI,
                   std::index_sequence<Ixs...>>::type; };


template <std::size_t ... Idx>
constexpr auto makeFooTupleH (std::index_sequence<Idx...> const &)
 { return std::make_tuple( foo<Idx>{} ... ); }

constexpr auto makeFooTuple ()
 { return makeFooTupleH(
      typename fooIndexList<0U, 100U, std::index_sequence<>>::type {}); }


int main ()
 {
   auto ft = makeFooTuple();

   static_assert( std::is_same<decltype(ft),
                  std::tuple<foo<2U>, foo<3U>, foo<5U>, foo<7U>>>{}, "!");
 }
Run Code Online (Sandbox Code Playgroud)

限制:

  • foo仅当未定义泛型时此解决方案才有效
  • 代码是C++14;如果你在 C++11 中需要它,那就有点复杂了
  • in 的上限makeFooTuple()不能太大,因为fooIndexList是递归的,因此受到编译器递归限制的限制;您可以绕过此限制,但需要模式代码。