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

标签 c++ c++11 templates template-meta-programming template-specialization

我有一个带有整数参数的模板,但基本模板被 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() };
    //...
};

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

template<> struct ItemTemplate<1>{
    static constexpr ItemID id{1};
    //..
};
template<> struct ItemTemplate<2>{
    static constexpr ItemID id{2};
    //...
};

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

最佳答案

我看到了一个方法,但前提是你忘记了 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> {};

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

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));

即:来自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; };

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

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 {}); }

在这个例子中上限是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>>>{}, "!");
 }

限制:

  • 此解决方案仅在通用 foo 时有效未定义
  • 代码是C++14;如果你在 C++11 中需要它,它会稍微复杂一些
  • makeFooTuple() 的上限不能是一个很大的数字,因为 fooIndexList是递归的,因此受限于编译器的递归限制;您可以绕过此限制,但需要模式代码。

关于C++ 如何基于部分特化创建 std::tuple 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46147579/

相关文章:

c++ - 如何更改 std::hash<> 中的默认种子?

c++ - 从初始化构造函数和赋值运算符创建的对象有什么区别?

c++ - 函数参数包的行为

c++ - QSqlQuery 插入字符

c++ - 返回对此的右值引用的正确方法

c# - 从 c++11 time_t 创建一个可转换为 .net DateTime 的字符串

python - django 缓存函数是否会生成模板

C++ 线程不检测全局变量变化

c++ - "int main(){(([](){})());}"如何是有效的 C++?

带有迭代器的 C++ 模板函数