根据非类型参数值推导出类型列表

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
 }