如何从可变参数模板中有条件地扩展容器的类型?

2023-12-30

我正在尝试为变体创建一个包装器,它允许我包装在另一种类型中传递的一堆类型,并将它们放入变体中。如何扩展元组的类型并根据该类型是否是容器有条件地将它们放入变体中?

template <typename T>
struct Wrapper
{};

template <typename>
struct has_expandable_types : std::false_type
{
};

template <template <typename...> class T, typename... Args>
struct has_expandable_types<T<Args...>> : std::true_type
{
};

template <template <typename> class T, typename... Types>
struct Variant_wrapper;
template <template <typename> class T, typename U, typename... Types>
struct Variant_wrapper_expanded;

template <template <typename> class T, typename... Types>
struct Variant_wrapper : std::variant<T<Types>...>
{
};

template <template <typename> class T, template <typename...> class U, typename... UTypes>
struct Variant_wrapper_expanded <T, U<UTypes...>> : Variant_wrapper<T, UTypes...>
{
};

using Types = std::tuple<int, float>;

int main()
{
    //std::variant< Wrapper<int>, Wrapper<float> >, OK
    Variant_wrapper<Wrapper, int, float> var; 

    //std::variant< Wrapper<int>, Wrapper<float> >, OK
    Variant_wrapper_expanded<Wrapper, Types> var2;

    //std::variant< Wrapper<int>, Wrapper<float> >, NOT OK
    // EXPECTED std::variant< Wrapper<int>, Wrapper<float>, Wrapper<double> >
    //can't capture a second parameter pack?
    Variant_wrapper_expanded<Wrapper, Types, double> var3;
}

理想的用法是这样的

//produces std::variant< Wrapper<std::string>, Wrapper<int>, Wrapper<float>, Wrapper<double> >
Variant_wrapper<Wrapper, std::string, Types, double> var; 
  1. 在 var3 示例中,尽管 UTypes 仅适用于容器类型,但我似乎无法使用第二个参数包来捕获元组之后的剩余类型。我怎样才能捕获剩余的类型?
  2. 将元组类型传递给Variant_wrapper包装整个元组,但我希望能够扩展保留的类型并将它们插入到变体中。我该如何进行扩展?

Update

我注意到附加要求std::tuple参数可以处于任何位置。以下允许传递类型或用 包裹的类型std::tuple以任意顺序作为模板参数。它只需要比原来的方法多一个专业化。

有四个专业需要处理:

  • 唯一的模板参数是std::tuple
  • 第一个模板参数不是std::tuple
  • 第一个模板参数是std::tuple但第二个不是
  • 第一个和第二个模板参数是std::tuple

基本情况是一个单一的std::tuple模板参数。这很容易翻译成包装的std::variant。所有其他情况都相互递归或递归到基本情况。

#include <type_traits>
#include <tuple>
#include <variant>

template<template<class> class T, class... Ts>
struct variant_wrapper_impl;

// The base case is that all types come packaged in a single tuple.
template<template<class> class T, class... Ts>
struct variant_wrapper_impl<T, std::tuple<Ts...>> {
    using type = std::variant<T<Ts>...>;
};

// The first type is not a tuple.
template<template<class> class T, class U, class... Us>
struct variant_wrapper_impl<T, U, Us...> {
    using type = typename variant_wrapper_impl<T, std::tuple<U>, Us...>::type;
};

// The first type is a tuple and the second type is not a tuple.
template<template<class> class T, class... Ts, class U, class... Us>
struct variant_wrapper_impl<T, std::tuple<Ts...>, U, Us...> {
    using type = typename variant_wrapper_impl<T, std::tuple<Ts..., U>, Us...>::type;
};

// The first type is a tuple and the second type is a tuple.
template<template<class> class T, class... Ts, class... Us, class... Ws>
struct variant_wrapper_impl<T, std::tuple<Ts...>, std::tuple<Us...>, Ws...> {
    using type = typename variant_wrapper_impl<T, std::tuple<Ts..., Us...>, Ws...>::type;
};

template<template<class> class T, class... Ts>
using variant_wrapper_t = typename variant_wrapper_impl<T, Ts...>::type;

template<class T>
class Wrapper {
};

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, int, float>,
              std::variant<Wrapper<int>, Wrapper<float>>>);

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, std::tuple<int, float>>,
              std::variant<Wrapper<int>, Wrapper<float>>>);

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, std::tuple<int, float>, double>,
              std::variant<Wrapper<int>, Wrapper<float>, Wrapper<double>>>);

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, char, std::tuple<int, float>, double>,
              std::variant<Wrapper<char>, Wrapper<int>, Wrapper<float>, Wrapper<double>>>);

int main(int argc, const char *argv[]) {
    return 0;
}

结束更新

我认为以下内容可以满足您的要求。关键思想是部分专业化variant_wrapper_imp处理元组子类型的展开。

#include <type_traits>
#include <tuple>
#include <variant>

template<template<class> class T, class... Ts>
struct variant_wrapper_impl;

// The simple case: W, int, float -> variant<W<int>, W<float>>
template<template<class> class T, class... Ts>
struct variant_wrapper_impl {
    using type = std::variant<T<Ts>...>;
};

// The tuple case: W, tuple<int, float> -> variant<W<int>, W<float>>
//
// We use the pattern matching of partial specialization to extract
// the tuple sub-types into Ts...
template<template<class> class T, class... Ts>
struct variant_wrapper_impl<T, std::tuple<Ts...>> {
    using type = std::variant<T<Ts>...>;
};

// The tuple+ case: W, tuple<int, float>, double -> variant<W<int>, W<float>, W<double>>
//
// We again use the pattern matching of partial specialization to
// extract the tuple sub-types into Ts.. and then recurse to the simple case.
template<template<class> class T, class... Ts, class... Us>
struct variant_wrapper_impl<T, std::tuple<Ts...>, Us...> {
    using type = typename variant_wrapper_impl<T, Ts..., Us...>::type;
};

template<template<class> class T, class... Ts>
using variant_wrapper_t = typename variant_wrapper_impl<T, Ts...>::type;

template<class T>
class Wrapper {
};

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, int, float>,
              std::variant<Wrapper<int>, Wrapper<float>>>);

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, std::tuple<int, float>>,
              std::variant<Wrapper<int>, Wrapper<float>>>);

static_assert(std::is_same_v<variant_wrapper_t<Wrapper, std::tuple<int, float>, double>,
              std::variant<Wrapper<int>, Wrapper<float>, Wrapper<double>>>);

int main(int argc, const char *argv[]) {
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从可变参数模板中有条件地扩展容器的类型? 的相关文章