根据非类型参数值推导出类型列表
Deduce a list of types based on non-type parameter value
我有一个 class 可以在可变参数模板参数中使用未定义数量的策略进行自定义:
template<typename... Features>
class A : public Features... {};
所述功能通过constexpr uint8_t
的位启用,例如:
0x1 --> X1 enabled
0x2 --> X2 enabled
0x3 --> X1, X2 enabled
0x4 --> X3 enabled
0x5 --> X1, X3 enabled
etc...
我如何编写辅助类型来推断正确的类型列表并将其转发给 class A
的可变参数模板参数?
使用 std::conditional
我可以写这样的东西,但是你可以想象它变得非常可怕。
using ExtendedA = A<MandatoryFeature, std::conditional_t<HasX1(), X1, std::monostate>;
或者我可以编写 uisng 助手类型和模板专业化
template<uint8_t value>
struct Features {};
template<>
struct Features<0x3> {
using type = std::tuple<X1, X2>;
};
但是我需要为 class A
的实例解压 Features<0x3>::type
,我不确定我应该怎么写。
要将您的标志转换为特征集合(元组),您可以使用类似于以下的代码:
template <std::size_t> struct Feature;
template <> struct Feature<0> { using type = X0; };
template <> struct Feature<1> { using type = X1; };
template <> struct Feature<2> { using type = X2; };
template <> struct Feature<3> { using type = X3; };
template <> struct Feature<4> { using type = X4; };
template <> struct Feature<5> { using type = X5; };
// ...
constexpr std::size_t FeaturesCount = /*..*/;
template <std::size_t Flag, std::size_t...Is>
auto CollectFeaturesImpl(std::index_sequence<Is...>)
-> decltype(std::tuple_cat(
std::conditional_t<
(Flag & (1U << Is)) != 0,
std::tuple<typename Feature<Is>::type>,
std::tuple<>>
{}...
))
template <std::size_t Flag>
using CollectFeatures_t =
decltype(CollectFeaturesImpl<Flag>(std::make_index_sequence<FeaturesCount>()));
But then I would need to unpack Features<0x3>::type
for class A
's instance, and I'm not sure how I should write that.
有了额外的层,你可能 "unpack" 元组:
template<typename Tuple> struct AFromTuple;
template<typename... Features>
struct AFromTuple<std::tuple<Features...>>
{
using type = A<Features...>;
};
using myA = AFromTuple<std::tuple<X1, X3>>::type; // A<X1, X3>
或
template<typename Tuple> struct FromTuple;
template<typename... Features>
struct FromTuple<std::tuple<Features...>>
{
template <template <typename...> Class C>
using map = C<Features...>;
};
using myA = FromTuple<std::tuple<X1, X3>>::map<A>; // A<X1, X3>
来不及玩了?
给定一个 X1
-X8
特征列表,我建议添加一个模板 class 将数字从 0 到 7 映射到 classes X1
-X8
并仅在第一个布尔模板参数为 true
时继承自 X1
-X8
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { };
template <> struct Feature<true, 1> : public X2 { };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
现在一个 Feature_helper
结构,它继承 std::uint8_t
数字中每一位的权利 Feature
,很简单
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ };
和A
变成
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ };
下面是一个完整的编译示例
#include <cstdint>
#include <utility>
#include <type_traits>
struct X1 { };
struct X2 { };
struct X3 { };
struct X4 { };
struct X5 { };
struct X6 { };
struct X7 { };
struct X8 { };
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { };
template <> struct Feature<true, 1> : public X2 { };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ };
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ };
int main()
{
static_assert( true == std::is_base_of_v<X1, A<5u>> );
static_assert( false == std::is_base_of_v<X2, A<5u>> );
static_assert( true == std::is_base_of_v<X3, A<5u>> );
static_assert( false == std::is_base_of_v<X4, A<5u>> );
static_assert( false == std::is_base_of_v<X5, A<5u>> );
static_assert( false == std::is_base_of_v<X6, A<5u>> );
static_assert( false == std::is_base_of_v<X7, A<5u>> );
static_assert( false == std::is_base_of_v<X8, A<5u>> );
}
-- 编辑--
OP观察
Found a downside, class inheriting Feature_helper
would not be a direct derived class of a Feature
. Meaning it cannot inherit constructors from X1
, X2
, ...
不确定是否理解您的要求,但我想您可以使用大量 using
.
来解决问题(如果 Feature
构造函数之间没有冲突)
在下面的示例中,我为 X1
添加了一个 int
构造函数,为 X2
添加了一个 long
构造函数。
观察X1() = default;
和X2() = default;
:添加显式构造函数默认的constructors/destructors被删除;所以你必须明确地重新默认它们(也许还有 copy/move 构造函数)。
#include <cstdint>
#include <utility>
#include <iostream>
#include <type_traits>
struct X1 { X1(int) { std::cout << "X1 constructor" << std::endl; }
X1() = default; };
struct X2 { X2(long) { std::cout << "X2 constructor" << std::endl; }
X2() = default; };
struct X3 { };
struct X4 { };
struct X5 { };
struct X6 { };
struct X7 { };
struct X8 { };
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { using X1::X1; };
template <> struct Feature<true, 1> : public X2 { using X2::X2; };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ using Feature<(u8 & (1 << Is)) != 0u, Is>::Feature...; };
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ using Feature_helper<u8>::Feature_helper; };
int main()
{
static_assert( true == std::is_base_of_v<X1, A<5u>> );
static_assert( false == std::is_base_of_v<X2, A<5u>> );
static_assert( true == std::is_base_of_v<X3, A<5u>> );
static_assert( false == std::is_base_of_v<X4, A<5u>> );
static_assert( false == std::is_base_of_v<X5, A<5u>> );
static_assert( false == std::is_base_of_v<X6, A<5u>> );
static_assert( false == std::is_base_of_v<X7, A<5u>> );
static_assert( false == std::is_base_of_v<X8, A<5u>> );
A<3u> a1(1); // call the X1(int) constructor
A<3u> a2(2l); // call the X2(long) constructor
}
我有一个 class 可以在可变参数模板参数中使用未定义数量的策略进行自定义:
template<typename... Features>
class A : public Features... {};
所述功能通过constexpr uint8_t
的位启用,例如:
0x1 --> X1 enabled
0x2 --> X2 enabled
0x3 --> X1, X2 enabled
0x4 --> X3 enabled
0x5 --> X1, X3 enabled
etc...
我如何编写辅助类型来推断正确的类型列表并将其转发给 class A
的可变参数模板参数?
使用 std::conditional
我可以写这样的东西,但是你可以想象它变得非常可怕。
using ExtendedA = A<MandatoryFeature, std::conditional_t<HasX1(), X1, std::monostate>;
或者我可以编写 uisng 助手类型和模板专业化
template<uint8_t value>
struct Features {};
template<>
struct Features<0x3> {
using type = std::tuple<X1, X2>;
};
但是我需要为 class A
的实例解压 Features<0x3>::type
,我不确定我应该怎么写。
要将您的标志转换为特征集合(元组),您可以使用类似于以下的代码:
template <std::size_t> struct Feature;
template <> struct Feature<0> { using type = X0; };
template <> struct Feature<1> { using type = X1; };
template <> struct Feature<2> { using type = X2; };
template <> struct Feature<3> { using type = X3; };
template <> struct Feature<4> { using type = X4; };
template <> struct Feature<5> { using type = X5; };
// ...
constexpr std::size_t FeaturesCount = /*..*/;
template <std::size_t Flag, std::size_t...Is>
auto CollectFeaturesImpl(std::index_sequence<Is...>)
-> decltype(std::tuple_cat(
std::conditional_t<
(Flag & (1U << Is)) != 0,
std::tuple<typename Feature<Is>::type>,
std::tuple<>>
{}...
))
template <std::size_t Flag>
using CollectFeatures_t =
decltype(CollectFeaturesImpl<Flag>(std::make_index_sequence<FeaturesCount>()));
But then I would need to unpack
Features<0x3>::type
forclass A
's instance, and I'm not sure how I should write that.
有了额外的层,你可能 "unpack" 元组:
template<typename Tuple> struct AFromTuple;
template<typename... Features>
struct AFromTuple<std::tuple<Features...>>
{
using type = A<Features...>;
};
using myA = AFromTuple<std::tuple<X1, X3>>::type; // A<X1, X3>
或
template<typename Tuple> struct FromTuple;
template<typename... Features>
struct FromTuple<std::tuple<Features...>>
{
template <template <typename...> Class C>
using map = C<Features...>;
};
using myA = FromTuple<std::tuple<X1, X3>>::map<A>; // A<X1, X3>
来不及玩了?
给定一个 X1
-X8
特征列表,我建议添加一个模板 class 将数字从 0 到 7 映射到 classes X1
-X8
并仅在第一个布尔模板参数为 true
X1
-X8
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { };
template <> struct Feature<true, 1> : public X2 { };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
现在一个 Feature_helper
结构,它继承 std::uint8_t
数字中每一位的权利 Feature
,很简单
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ };
和A
变成
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ };
下面是一个完整的编译示例
#include <cstdint>
#include <utility>
#include <type_traits>
struct X1 { };
struct X2 { };
struct X3 { };
struct X4 { };
struct X5 { };
struct X6 { };
struct X7 { };
struct X8 { };
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { };
template <> struct Feature<true, 1> : public X2 { };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ };
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ };
int main()
{
static_assert( true == std::is_base_of_v<X1, A<5u>> );
static_assert( false == std::is_base_of_v<X2, A<5u>> );
static_assert( true == std::is_base_of_v<X3, A<5u>> );
static_assert( false == std::is_base_of_v<X4, A<5u>> );
static_assert( false == std::is_base_of_v<X5, A<5u>> );
static_assert( false == std::is_base_of_v<X6, A<5u>> );
static_assert( false == std::is_base_of_v<X7, A<5u>> );
static_assert( false == std::is_base_of_v<X8, A<5u>> );
}
-- 编辑--
OP观察
Found a downside, class inheriting
Feature_helper
would not be a direct derived class of aFeature
. Meaning it cannot inherit constructors fromX1
,X2
, ...
不确定是否理解您的要求,但我想您可以使用大量 using
.
Feature
构造函数之间没有冲突)
在下面的示例中,我为 X1
添加了一个 int
构造函数,为 X2
添加了一个 long
构造函数。
观察X1() = default;
和X2() = default;
:添加显式构造函数默认的constructors/destructors被删除;所以你必须明确地重新默认它们(也许还有 copy/move 构造函数)。
#include <cstdint>
#include <utility>
#include <iostream>
#include <type_traits>
struct X1 { X1(int) { std::cout << "X1 constructor" << std::endl; }
X1() = default; };
struct X2 { X2(long) { std::cout << "X2 constructor" << std::endl; }
X2() = default; };
struct X3 { };
struct X4 { };
struct X5 { };
struct X6 { };
struct X7 { };
struct X8 { };
template <bool, std::size_t>
struct Feature
{ };
template <> struct Feature<true, 0> : public X1 { using X1::X1; };
template <> struct Feature<true, 1> : public X2 { using X2::X2; };
template <> struct Feature<true, 2> : public X3 { };
template <> struct Feature<true, 3> : public X4 { };
template <> struct Feature<true, 4> : public X5 { };
template <> struct Feature<true, 5> : public X6 { };
template <> struct Feature<true, 6> : public X7 { };
template <> struct Feature<true, 7> : public X8 { };
template <std::uint8_t, typename = std::make_index_sequence<8u>>
struct Feature_helper;
template <std::uint8_t u8, std::size_t ... Is>
struct Feature_helper<u8, std::index_sequence<Is...>>
: public Feature<(u8 & (1 << Is)) != 0u, Is>...
{ using Feature<(u8 & (1 << Is)) != 0u, Is>::Feature...; };
template <std::uint8_t u8>
class A : public Feature_helper<u8>
{ using Feature_helper<u8>::Feature_helper; };
int main()
{
static_assert( true == std::is_base_of_v<X1, A<5u>> );
static_assert( false == std::is_base_of_v<X2, A<5u>> );
static_assert( true == std::is_base_of_v<X3, A<5u>> );
static_assert( false == std::is_base_of_v<X4, A<5u>> );
static_assert( false == std::is_base_of_v<X5, A<5u>> );
static_assert( false == std::is_base_of_v<X6, A<5u>> );
static_assert( false == std::is_base_of_v<X7, A<5u>> );
static_assert( false == std::is_base_of_v<X8, A<5u>> );
A<3u> a1(1); // call the X1(int) constructor
A<3u> a2(2l); // call the X2(long) constructor
}