【问题标题】:How to transform Parameter Pack into something else than std::tuple?如何将参数包转换为 std::tuple 以外的其他东西?
【发布时间】:2020-04-21 02:40:50
【问题描述】:

最好用一个例子来解释:

template <typename T1, typename T2>
struct OnePair
{
    using TupleOfArgs = std::tuple<T1, T2>;
    using TupleOfPairs = std::tuple<std::pair<T1, T2>>;
};

template <typename T1, typename T2, typename T3, typename T4>
struct TwoPairs
{
    using TupleOfArgs = std::tuple<T1, T2, T3, T4>;
    using TupleOfPairs = std::tuple<std::pair<T1, T2>, std::pair<T3, T4>>;
};

template <typename... Args>
struct NPairs
{
    using TupleOfArgs = std::tuple<Args...>;
//  using TupleOfPairs = ???
};

OnePair 定义了一个包含一对的元组。 TwoPairs 定义了一个有两对的元组。

如何在 NPairs 中定义 TupleOfPairs,以便将参数包转换为 std::tuple of pairs?

是否可以使用 std 库来实现?也许用 boost::mpl?

两个答案,都很棒。 @chris 使用迭代方法,而 @aschepler 使用递归解决方案。 就个人而言,我发现递归解决方案更容易理解。

【问题讨论】:

    标签: c++ template-meta-programming boost-mpl


    【解决方案1】:

    您可以使用熟悉的索引序列技巧,但大小只有一半 (live example):

    static constexpr auto get_tuple() {
        constexpr auto N = sizeof...(Args);
        static_assert(N%2 == 0);
    
        using ArgsTuple = std::tuple<Args...>;
        auto impl = []<std::size_t... Is>(std::index_sequence<Is...>) {
            return std::tuple<
                // Is goes from 0 to N/2, representing Ith pair
                std::pair<std::tuple_element_t<Is*2, ArgsTuple>, std::tuple_element_t<Is*2 + 1, ArgsTuple>>...
            >{};
        };
    
        return impl(std::make_index_sequence<N/2>{});
    }
    
    using TupleOfArgs = decltype(get_tuple());
    

    如果您没有 C++20,则必须将 lambda 扩展为一个函数,而不是能够使用漂亮的内联索引序列扩展,但核心仍然有效。使用类型列表而不是 std::tuple 可能会更简洁一些,但它是可行的。

    【讨论】:

    • 不错的解决方案。作为附带说明,我无法使其在 vs2017 上运行。
    • @Gils,C++20 模板化的 lambda 绝对不会。我不记得元编程支持是什么时候在 VS 中真正增加的,但它的其余部分可能与分离到不同函数的 lambda 一起工作。
    • 当然不会。但我接受了你的建议,并试图将 lambda 提取到一个函数中。我收到'NPairs&lt;Args...&gt;::get_tuple': a function that returns 'auto' cannot be used before it is defined 错误。我没有花太多时间在上面。递归解决方案对我来说更容易使用,因为我正在使用 C++11。非常感谢您的回答。我真的很喜欢它。
    • @Gils,啊,这个sn-p依赖于C++14的返回类型推导。当然,您可以将其重构为符合 C++11 的内容,但递归方法可能更具可读性。
    【解决方案2】:

    这是一种使用递归助手的简单方法:

    template <typename PairsTuple, typename... Ts>
    struct ZipPairs;
    
    template <typename PairsTuple>
    struct ZipPairs<PairsTuple> { using type = PairsTuple; };
    
    template <typename... Pairs, typename T1, typename T2, typename... Ts>
    struct ZipPairs<std::tuple<Pairs...>, T1, T2, Ts...>
    {
        using type = typename ZipPairs<
            std::tuple<Pairs..., std::pair<T1, T2>>, Ts...>::type;
    };
    
    template <class... Args>
    struct NPairs
    {
        static_assert(sizeof...(Args) % 2 == 0);
        using TupleOfArgs = std::tuple<Args...>;
        using TupleOfPairs = typename ZipPairs<std::tuple<>, Args...>::type;
    };
    

    【讨论】:

    • 不错的解决方案。我试图用 boost::mpl::list 替换 std::tuple ,但由于某种原因,它不起作用。我收到以下错误:error: wrong number of template arguments (21, should be at least 0)。知道出了什么问题吗?
    • 我认为你不应该像这样对元组直接操作 MPL 模板参数,而是使用像 boost::mpl::insertboost::mpl::push_back 这样的元函数。 (看起来mpl::list 并不是真正的可变参数,而是有一堆虚拟的默认参数,可能是因为它最初是 C++03 / 为了 C++03 兼容性。)并且mpl::list 不支持 push_back .有办法让mpl::list 工作,但mpl::vector 将是更直接的翻译:coliru.stacked-crooked.com/a/ed600f95e53df5e0
    • 非常感谢您的解释。你的例子很有帮助。很抱歉没有尽快回复,我花了一些时间才回到那个问题。
    猜你喜欢
    • 2012-12-11
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多