并行迭代 2 个模板参数包

问题描述 投票:0回答:2

我开始实现一个非常灵活的里程表。它可能有几个磁盘,每个不同的磁盘上的值数量甚至不同。而且,作为扩展,甚至每个磁盘上值的数据类型也可能不同。

这一切都用一个班来实现。模板参数的数量定义了类的行为。

  • 1 模板参数:如
    Odomoter<int>
    应导致里程表在每个磁盘上具有
    int
    值。生成的内部数据类型将是
    std::vector<std::vector<int>>
  • 2个或更多模板参数:模板参数的个数将定义里程计的单盘数。每个磁盘都有模板参数的数据类型。在
    Odometer<char, int, double>
    的情况下,这将导致数据类型
    std::tuple<std::vector<char>, std::vector<int>, std::vector<double>>

现在,我想添加一个可变参数构造函数,我可以在其中添加任何数据。当然,参数的类型和数量必须匹配。我暂时省略检查,稍后再添加。

所以,现在我有了一个模板化的可变参数类和一个可变参数构造函数。所以,我有类的参数包和构造函数的参数包。

现在我需要同时并行迭代两个参数包的元素。

请参阅下面的代码示例以说明问题(我删除了课程中的大部分代码,只是为了向您展示问题):

#include <vector>
#include <tuple>
#include <list>
#include <initializer_list>

template<typename...Ts> 
struct Odometer {

    static constexpr bool IsTuple = ((std::tuple_size<std::tuple<Ts...>>::value) > 1);

    template<typename...Ts>
    using Tuples = std::tuple<std::vector<Ts>...>;

    template<typename...Ts>
    using MyType = std::tuple_element_t<0, std::tuple<Ts...>>;

    template<typename...Ts>
    using Vectors = std::vector<std::vector<MyType<Ts...>>>;

    template<typename...Ts>
    using Disks = std::conditional<IsTuple, Tuples<Ts...>, Vectors<Ts...>>::type;

    Disks<Ts...> disks{};

    template <typename...Args>
    Odometer(Args...args) {

        if constexpr (IsTuple) {
            // Here disk is a std::tuple<std::vector<char>, std::vector<int>, std::vector<double>>
            ([&] {
            //std::vector<MyType<Ts...>> disk{};   // Does not work. Or would always be a std::vector<char>
            if constexpr (std::ranges::range<Args>) {
                //for (const auto& r : args)       // Does not work
                    //disk.push_back(r);           // Does not work
            }
            else {
                //disk.push_back(args); // Does not work
            } } (), ...);
        }
        else {
            ([&] {
                disks.push_back({});
                if constexpr (std::ranges::range<Args>) {
                    for (const auto& r : args)
                        disks.back().push_back(r);
                }
                else {
                    disks.back().push_back(args);
                } } (), ...);
        }
    }
};
int main() {
    
    Odometer<char, int, double> odo2('a', std::vector{1,2,3}, std::list{4.4, 5.5});
}

我可以使用折叠表达式迭代构造函数的参数包。我也可以使用

std::apply
。但是,我还需要遍历由类模板参数定义的“磁盘”的元组元素。

我不想使用递归模板。


所以,我需要同时并行迭代2个参数包。这怎么可能?


我现在唯一的想法是使用带有

std::index_sequence
的辅助类,但我不知道。

请注意。稍后将检查参数包中的元素数量和类型。

c++ variadic-templates fold-expression
2个回答
1
投票

我现在唯一的想法是使用一个带有

std::index_sequence
,但我不知道。

可以使用template lambda展开

index_sequence
,通过
std::get<Is>
得到对应的元组元素:

static_assert(sizeof...(Args) == sizeof...(Ts));
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
  ([&] {
    auto& disk = std::get<Is>(disks);
    if constexpr (std::ranges::range<Args>)
      for (const auto& elem : args)
        disk.push_back(elem);
    else
      disk.push_back(args);
  } (), ...);
}(std::index_sequence_for<Args...>{});

减少示例的演示


1
投票

你不需要手动迭代,你只需要一个函数将每个参数转换成向量:

template <typename T, typename Arg>
std::vector<T> as_vector(Arg&& arg)
{
    if constexpr (std::ranges::range<std::decay_t<Arg>>) {
        return {std::begin(arg), std::end(arg)};
    } else {
        return {std::forward<Arg>(arg)};
    }
}

然后你的构造函数很简单(应该更复杂:SFINAE for nearly copy constructor with forwarding reference to avoid current copies):

template <typename...Args>
Odometer(Args... args) : disks{as_vector<Ts>(args)...} {}

演示

请注意,

as_vector<Ts>(args)...
将两个包与一个
...
一起使用,因此它们的尺寸应该相同。

© www.soinside.com 2019 - 2024. All rights reserved.