取决于类模板参数的条件变量类型

Conditional variant type depending on class template arguments

提问人:paulinho 提问时间:10/7/2023 最后编辑:paulinho 更新时间:10/7/2023 访问量:105

问:

我有一个类模板,本质上想为将向 API 用户公开的变体类型创建一个模板别名:

template <bool InsertsAllowed, DeletesAllowed>
class Executor {
   ...

 public:
   // Ideally, would be
   // std::variant<ReadRequest, {InsertRequest only if InsertsAllowed}, {DeleteRequest only if DeletesAllowed}>
   // e.g. InsertsAllowed = true, DeletesAllowed = False ==> std::variant<ReadRequest, InsertRequest>
   // InsertsAllowed = true, DeletesAllowed = True ==> std::variant<ReadRequest, InsertRequest, DeleteRequest>
   using RequestType = ...

   makeRequest(const RequestType &r);

   ...
}

到目前为止,我能想到的最好的解决方案是

template <bool InsertsAllowed, DeletesAllowed>
class Executor {
   ...

 public:
   struct BAD_TYPE = {};
   using RequestType = std::variant<
      ReadRequest,
      std::conditional<InsertsAllowed, InsertRequest, BAD_TYPE>,
      std::conditional<DeleletesAllowed, DeleteRequest, BAD_TYPE>>;

   makeRequest(const RequestType &r);

   ...
}

但这并不能完全实现我的目标,无论如何都不是最干净的解决方案。

我需要能够引用 ,所以不幸的是我不能用 替换。此外,我想避免堆叠一堆条件来实现我的目标,以防我需要添加更多模板参数以达到类似的效果。RequestTypeBAD_TYPEvoid

C++ 模板 元编程

评论

0赞 paulinho 10/7/2023
@TedLyngmo 嗯,我不太确定你在说什么,但我确实用更正确的术语更新了帖子
0赞 Ted Lyngmo 10/7/2023
好多了。当人们知道别人在问什么时,它会有所帮助。
0赞 Pepijn Kramer 10/7/2023
你需要的是一个类型列表(这就是它的调用方式)。阅读:codingwiththomas.com/blog/getting-started-with-typelists

答:

1赞 Eugene 10/7/2023 #1

首先,您需要创建一个元函数,以有条件地将另一种类型附加到 的模板参数列表中。让我们命名它:std::variantcondit_append

#include <type_traits>
#include <variant>

template<bool cond, typename A, typename B>
struct condit_append;

template<bool cond, typename U, typename ...T>
struct condit_append<cond, std::variant<T...>, U>
{
    using type = std::conditional_t<cond,
        std::variant<T..., U>,
        std::variant<T...>
        >;
};

接下来,让我们为它创建一个方便别名,类似于标准特征的操作方式:

template<bool cond, typename ...T>
using condit_append_t = typename condit_append<cond, T...>::type;

最后,将其用于您的课程:

    class ReadRequest;
    class InsertRequest;
    class DeleteRequest;
    template <bool InsertsAllowed, bool DeletesAllowed>
    class Executor {
        using Type1 = std::variant<ReadRequest>;
        using Type2 = condit_append_t<InsertsAllowed,Type1, InsertRequest>;
    public:
        using RequestType = condit_append_t<DeletesAllowed,Type2, DeleteRequest>;
         //...
    };
1赞 Jarod42 10/7/2023 #2

std::tuple已经具有函数 std::tuple_cat

因此,您可能有:

using RequestTypeTuple = decltype(std::tuple_cat(
     std::declval<std::tuple<ReadRequest>>(),
     std::declval<std::conditional_t<InsertsAllowed,
                                     std::tuple<InsertRequest>, std::tuple<>>>(),
     std::declval<std::conditional_t<DeleletesAllowed,
                                     std::tuple<DeleteRequest>, std::tuple<>>>()
     ));

然后是一个简单的“映射”特征,将元组转换为变体:

template <typename> struct to_variant_impl;

template <typename... Ts> struct to_variant_impl<std::tuple<Ts...>>
{
    using type = std::variant<Ts...>;
};
template <typename T>
using to_variant_t = typename to_variant_impl<T>::type;

所以你的班级将是:

template <bool InsertsAllowed, DeletesAllowed>
class Executor {
    using RequestTypeTuple = decltype(std::tuple_cat(
        std::declval<std::tuple<ReadRequest>>(),
        std::declval<std::conditional_t<InsertsAllowed,
                                       std::tuple<InsertRequest>, std::tuple<>>>(),
        std::declval<std::conditional_t<DeleletesAllowed,
                                        std::tuple<DeleteRequest>, std::tuple<>>>()
     ));
public:
   using RequestType = to_variant_t<RequestTypeTuple>;

// ...
};