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