c ++ 17通过生成预先声明的类型列表的笛卡尔积来制作std :: variant

A A*_*laa 6 c++ templates template-meta-programming variadic-templates c++17

假设我有一个三个模板类型参数的类.

template<typename Transformer, typename Criteria, typename Strategy>
struct ConfiguredPipeline {};
Run Code Online (Sandbox Code Playgroud)

并在以后的实例化中使用以下类ConfiguredPipeline:

template<typename...>
struct CriteriaList
{
};
using SupportedCriteria = CriteriaList<ChiSquared, Cosine>;

template<typename...>
struct StrategiesList
{
};
using SupportedStrategies = StrategiesList<Voting>;

template<typename...>
struct TransformerList
{
};
using SupportedTransformer = TransformerList<AAGrouper11, AAGrouper15>;
Run Code Online (Sandbox Code Playgroud)

我怎样才能生成std::variant相当于:

using PipelineVariant = std::variant< 
ConfiguredPipeline< ChiSquared , Voting , AAGrouper11 > ,
ConfiguredPipeline< ChiSquared , Voting , AAGrouper15 > ,
ConfiguredPipeline< Cosine , Voting , AAGrouper11 > ,
ConfiguredPipeline< Cosine , Voting , AAGrouper15 >>;
Run Code Online (Sandbox Code Playgroud)

通过简单地调用如下函数:

using PipelineVariant = makeVariant< ConfiguredPipeline , SupportedCriteria , SupportedStrategies, SupportedTransformaers >;
Run Code Online (Sandbox Code Playgroud)

max*_*x66 7

我假设decltype(),std::declval()并且std::tuple_cat()可以帮助你很多.

给定以下几个重载模板声明(观察:声明,未定义,遵循std::declval()示例)函数,使类型的笛卡尔积和折叠(感谢std::tuple_cat())在单个std::tuple

template <template <typename...> class C, typename ... Ts>
constexpr std::tuple<C<Ts...>> tupleExpand (std::tuple<Ts...> const &);

template <template <typename...> class C, typename ... Ts,
          template <typename...> class C0, typename ... Ls,
          typename ... Cs>
constexpr auto tupleExpand (std::tuple<Ts...> const &, C0<Ls...> const &,
                            Cs const & ... cs)
   -> decltype(std::tuple_cat(
         tupleExpand<C>(std::declval<std::tuple<Ts..., Ls>>(), cs...)...));
Run Code Online (Sandbox Code Playgroud)

和以下简单模板函数(再次:仅声明)转换std::tuple类型列表中的类型std::variant列表

template <typename ... Ts>
constexpr std::variant<Ts...> tupleToVariant (std::tuple<Ts...> const &);
Run Code Online (Sandbox Code Playgroud)

你可以写一个MakeVariant类如下

template <template <typename...> class C, typename ... Ts>
struct MakeVariant
 {
   using type = decltype(tupleToVariant(std::declval<
                   decltype(tupleExpand<C>(std::declval<std::tuple<>>(),
                                           std::declval<Ts>()...))>()));
 };
Run Code Online (Sandbox Code Playgroud)

并有一个简单的using帮手

template <template <typename...> class C, typename ... Ts>
using MakeVariantType = typename MakeVariant<C, Ts...>::type;
Run Code Online (Sandbox Code Playgroud)

您可以PipelineVariant按如下方式定义类型

using PipelineVariant = MakeVariantType<ConfiguredPipeline,
                                        SupportedCriteria,
                                        SupportedStrategies,
                                        SupportedTransformers>;
Run Code Online (Sandbox Code Playgroud)

以下是完整的编译示例

#include <tuple>
#include <variant>

template <typename, typename, typename>
struct ConfiguredPipeline
 { };

struct ChiSquared {};
struct Cosine     {};

template <typename...>
struct CriteriaList
 { };

using SupportedCriteria = CriteriaList<ChiSquared, Cosine>;

struct Voting {};

template <typename...>
struct StrategiesList
 { };

using SupportedStrategies = StrategiesList<Voting>;

struct AAGrouper11 { };
struct AAGrouper15 { };

template <typename...>
struct TransformerList
 { };

using SupportedTransformers = TransformerList<AAGrouper11, AAGrouper15>;

template <template <typename...> class C, typename ... Ts>
constexpr std::tuple<C<Ts...>> tupleExpand (std::tuple<Ts...> const &);

template <template <typename...> class C, typename ... Ts,
          template <typename...> class C0, typename ... Ls,
          typename ... Cs>
constexpr auto tupleExpand (std::tuple<Ts...> const &, C0<Ls...> const &,
                            Cs const & ... cs)
   -> decltype(std::tuple_cat(
         tupleExpand<C>(std::declval<std::tuple<Ts..., Ls>>(), cs...)...));

template <typename ... Ts>
constexpr std::variant<Ts...> tupleToVariant (std::tuple<Ts...> const &);

template <template <typename...> class C, typename ... Ts>
struct MakeVariant
 {
   using type = decltype(tupleToVariant(std::declval<
                           decltype(tupleExpand<C>(std::declval<std::tuple<>>(),
                                                   std::declval<Ts>()...))>()));
 };

template <template <typename...> class C, typename ... Ts>
using MakeVariantType = typename MakeVariant<C, Ts...>::type;

using PipelineVariant = MakeVariantType<ConfiguredPipeline,
                                        SupportedCriteria,
                                        SupportedStrategies,
                                        SupportedTransformers>;

int main () 
 {
   static_assert(std::is_same<PipelineVariant, 
                              std::variant<ConfiguredPipeline<ChiSquared, Voting, AAGrouper11>,
                                           ConfiguredPipeline<ChiSquared, Voting, AAGrouper15>,
                                           ConfiguredPipeline<Cosine, Voting, AAGrouper11>,
                                           ConfiguredPipeline<Cosine, Voting, AAGrouper15>>>::value);
 }
Run Code Online (Sandbox Code Playgroud)