修改元组元素的类型

Modifying types of tuple elements

将类型列表作为可变参数模板参数,可以很容易地对它们执行任意类型操作,从而获得修改类型的元组。例如。要用自定义包装器 class 包装每个元素,可以这样做:

template<typename T> class Wrapper {};

template<typename ...Values>
using WrappedValues = std::tuple<Wrapper<Values>...>;

using NewTuple = WrappedValues<int, std::string, char>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");

当 std::tuple 的专业化为 "input" 时,如何做同样的事情?例如。应该放置什么而不是“???”使以下代码可编译:

template<typename T> class Wrapper {};

template<typename Tuple>
using WrappedTupleElements = ???;

using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");

我知道使用 std::tuple_element 和递归模板实例化访问元组元素类型的可能性,但我不知道如何将以这种方式创建的类型收集到一个元组中。

首选纯 C++14 答案,但也欢迎使用 C++17、广泛可用的 TSes 或外部库(例如 boost)的提案。

为什么不使用允许特化的附加结构模板:

#include <string>
#include <tuple>
#include <type_traits>

template<typename T> class Wrapper {};

template<typename Tuple>
struct WrappedTupleElements;

template <class... Values>
struct WrappedTupleElements<std::tuple<Values...>> {
    using type = std::tuple<Wrapper<Values>...>;
};

int main() {
    using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>::type; 
    static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}

[live demo]

一个常见的习惯用法(例如 boost mpl)是使用元函数的概念。

元函数是一个模板class,它声明一个名为结果的类型,该类型产生在输入上应用元函数的结果类型。

#include <string>
#include <tuple>
#include <type_traits>

template<typename T> class Wrapper {};

namespace metafunction
{
    template<class MetaFunction> using result_of = typename MetaFunction::result;

    // meta-function which yields Wrapper<Element> from Element
    // type: unary metafunction
    // arg1 = the type to wrap
    // returns Wrapper<arg1>
    //
    template<class Element>
    struct apply_wrapper
    {
        using result = Wrapper<Element>;
    };

    template<class Tuple, template<class> class Function>
    struct transform_elements;

    // meta-function which takes a tuple and a unary metafunction
    // and yields a tuple of the result of applying the metafunction
    // to each element_type of the tuple.
    // type: binary metafunction
    // arg1 = the tuple of types to be wrapped
    // arg2 = the unary metafunction to apply to each element_type
    // returns tuple<result_of<arg2<element>>...> for each element in arg1

    template<class...Elements, template<class> class UnaryMetaFunction>
    struct transform_elements<std::tuple<Elements...>, UnaryMetaFunction>
    {
        template<class Arg> using function = UnaryMetaFunction<Arg>;
        using result = std::tuple
        <
            result_of<function<Elements>>...
        >;
    };
}

int main() {
    using input_type = std::tuple<int, std::string, char>;

    using namespace metafunction;

    using wrapped_tuple = result_of<transform_elements<input_type, apply_wrapper>>;

    static_assert(std::is_same<wrapped_tuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}