从给定的嵌套boost-variant类型创建新的boost-variant类型

Ale*_*ph0 7 c++ boost-variant boost-mpl template-meta-programming variadic-templates

假设我有一个嵌套boost::variant类型TNested包含一些类型和一些其他boost::variant类型(它本身不能再包含boost::variant types,因此不会有递归).

我正在寻找一个模板别名flatten<TNested>,它将评估一个boost::variant没有嵌套boost::variant的类型,例如TFlatten,虽然可能的重复类型被删除,例如int只发生一次.

我真的不知道,如果这可以以某种方式完成.

#include <boost/variant.hpp>
#include <boost/any.hpp>
#include <iostream>

struct Person;

typedef boost::variant<int, double, boost::variant<std::string, int>, boost::variant<Person>> TNested;
typedef boost::variant<int, double, std::string, Person> TFlatten;

template<typename NestedVariant>
using flatten = //???

int main() {
    flatten<TNested> x; 
    std::cout << typeid(x) == typeid(TFlatten) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

Vit*_*meo 5

魔杖盒示例

这是一个有效的基于 C++11 递归模板的解决方案,适用于您的示例:

using nested = variant<int, double, variant<std::string, int>, variant<Person>>;
using flattened = variant<int, double, std::string, int, Person>;

static_assert(std::is_same<flatten_variant_t<nested>, flattened>{}, "");
Run Code Online (Sandbox Code Playgroud)

大概的概念:

std::tuple用作泛型类型列表并std::tuple_cat用于连接类型列表。

flatten_variant<TResult, Ts...>定义了一个递归元函数,它执行以下操作:

  • TResult 将填充扁平类型并在递归结束时返回。

    • Ts...为空时递归结束。
  • 非变体类型附加到TResult.

  • 变体类型被解包,它们所有的内部类型都被递归地展平,然后附加到TResult.


执行:

namespace impl
{
    // Type of the concatenation of all 'Ts...' tuples.
    template <typename... Ts>
    using cat = decltype(std::tuple_cat(std::declval<Ts>()...));

    template <typename TResult, typename... Ts>
    struct flatten_variant;

    // Base case: no more types to process.
    template <typename TResult>
    struct flatten_variant<TResult>
    {
        using type = TResult;
    };

    // Case: T is not a variant.
    // Return concatenation of previously processed types,
    // T, and the flattened remaining types.
    template <typename TResult, typename T, typename... TOther>
    struct flatten_variant<TResult, T, TOther...>
    {
        using type = cat<TResult, std::tuple<T>,
                         typename flatten_variant<TResult, TOther...>::type>;
    };

    // Case: T is a variant.
    // Return concatenation of previously processed types,
    // the types inside the variant, and the flattened remaining types.    
    // The types inside the variant are recursively flattened in a new
    // flatten_variant instantiation.
    template <typename TResult, typename... Ts, typename... TOther>
    struct flatten_variant<TResult, variant<Ts...>, TOther...>
    {
        using type =
            cat<TResult, typename flatten_variant<std::tuple<>, Ts...>::type,
                typename flatten_variant<TResult, TOther...>::type>;
    };

    template<typename T>
    struct to_variant;

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

template <typename T>
using flatten_variant_t =
    typename impl::to_variant<
        typename impl::flatten_variant<std::tuple<>, T>::type
    >::type;
Run Code Online (Sandbox Code Playgroud)