如何从可变模板包和使用相同模板类型的对象列表中初始化向量元组?

How to initialize tuple of vectors out of a variadic template pack and list of objects which use the same template types?

下面是对我打算做什么的简单描述以及我正在处理它的代码。

我有一个结构 TEmitter<Ts...>,其中 Ts... 是存储在 TEmitter.

内部的 std::tuple 中的组件列表

假设有两个发射器:

 e1<A, B, C>
 e2<A, B, C>

我想创建一个 class EmitterMemory<Ts...>,它在构造函数中采用 std::initializer_list<TEmitter<Ts...>>,将每个 TEmitter 的组件分开,然后将它们放入std::vectors 存储在元组中,例如:

EmitterMemory<A, B, C> {e1, e2}

(has a) stl::tuple
        |-stl::vector<A> = [A(e1), A(e2)]
        |-stl::vector<B> = [B(e1), B(e2)]
        |-stl::vector<C> = [C(e1), C(e2)]

现在在下面的代码中,我想我已经设法正确地声明了 class EmitterMemory,我只是不知道如何从构造函数中初始化 std::tuple<std::vector<Ts>...>参数 std::initializer_list<TEmitter<Ts...>>.

我需要以某种方式遍历可变参数模板 Ts... 中声明的组件,并为每个组件调用 get<T>(TEmitter<Ts...>) 并将结果放入 std::vector<T>.

我在 main() 函数的最后 4 行中手动执行的操作。

我需要一些帮助来解决这个问题,所以它在 class 构造函数中自动完成。 你能告诉我它是否可行吗?

代码

#include <concepts>
#include <functional>
#include <iostream>

struct ComponentBase{};

template<typename T>
concept is_a_component = std::is_base_of_v<ComponentBase, T>;
template<typename ...T>
concept is_many_components = (is_a_component<T> && ...);

namespace components {
    struct Position : ComponentBase {};
    struct Shoot : ComponentBase {};
    struct Active : ComponentBase {};
    struct Emission : ComponentBase {};
}

struct EmitterBase {};
template<typename T>
concept is_an_emitter = std::is_base_of_v<EmitterBase, T>;

template<is_many_components... Ts>
class TEmitter : EmitterBase {    
    using Components = std::tuple<Ts...>;
public:
    explicit TEmitter(int id) : id{id} {};
    int id{0};    
    Components components = std::make_tuple(Ts()...);
};

template<is_a_component T>
auto &get(is_an_emitter auto &emitter) {
    return std::get<T>(emitter.components);
}

struct EmitterMemoryBase {};

template<is_many_components... Ts>
class EmitterMemory : EmitterMemoryBase {
    using TupleOfVectors = std::tuple<std::vector<Ts>...>;
public:
    static constexpr std::size_t AmountComponents = sizeof...(Ts);
    TupleOfVectors components;

    EmitterMemory(std::initializer_list<TEmitter<Ts...>> emitters)
        : components { std::make_tuple<>(std::vector<Ts>{}...) } 
        { };
};

template<typename ...Ts>
concept is_a_memory = std::is_base_of_v<EmitterMemoryBase, Ts...> ;

template<is_a_component T>
auto &get(is_a_memory auto &memory) {
    return std::get<std::vector<T>>(memory.components);
}

int main () {
    using namespace components;
    auto e1 = TEmitter<Position, Shoot, Active>{1};
    auto e2 = TEmitter<Position, Shoot, Active>{2};

    auto &e1_position_component = get<Position>(e1);
    auto &e2_position_component = get<Position>(e2);
    auto memory = EmitterMemory<Position, Shoot, Active>({e1, e2});        
    auto &position_vector = get<Position>(memory);
    position_vector.emplace_back(e1_position_component);
    position_vector.emplace_back(e2_position_component);
}

不完全像你问的那样...使用可变元素列表而不是初始化列表...但是添加辅助方法,应该按如下方式工作

template <typename T, typename ... Ems>
static std::vector<T> getVect(Ems const & ... ems)
 { return { get<T>(ems)... }; }

template <typename ... Ems>
EmitterMemory (Ems const & ... ems)
    : components { getVect<Ts>(ems...)... } 
    { }

显然你必须初始化memory

auto memory = EmitterMemory<Position, Shoot, Active>(e1, e2); 

没有括号。

如果您真的想使用 std::initializer_list(但为什么呢?),您可以将它发送到 getVect() 并使用经典的 range-for 在方法体内解压它。

未申请的奖金

使用可变参数构造函数,您可以添加显式推导指南

template <typename ... Es, typename ... Ts>
EmitterMemory (TEmitter<Es...>, Ts...) -> EmitterMemory<Es...>;

所以你可以像下面这样简单地初始化memory

auto memory = EmitterMemory(e1, e2);

因为 is_main_components... Ts 模板参数(在本例中为 Position, Shoot, Active)是从第一个参数(e1)推导出来的。

显然,如果您至少使用一个参数来初始化 memory,则此方法有效。