在C++中遍历元组的类型

6rqinv9w  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(135)

我想迭代元组的类型,而不是它的元素。
假设你有一个泛型的基类接口Controller,你想有一个指针向量(* 为了可读性,我使用raw pointer而不是smart *):std::vector<Controller*> ctrls;
现在你想把Controller接口的许多实现添加到这个向量中,所以你可以这样做:

ctrls.emplace_back(new MouseCtrl());
ctrls.emplace_back(new KeyboardCtrl());
ctrls.emplace_back(new ScreenCtrl());
(... and so on)

但这是丑陋的,并且不完全可扩展(如在开闭原则中),所以最好有一个元组:using Controllers = std::tuple<MouseCtrl, KeyboardCtrl, ScreenCtrl>;,然后在一些init函数中,迭代这些类型:

for (T : Controllers> { 
   ctrls.emplace_back(new T());
}

显然,上面的代码不是有效的C++语法。所以问题是:如何做到这一点?.我已经研究了std::apply和std::visit / std::variant,但我不知道如何做到这一点(它们迭代元素,而不是类型)。

eanckbw9

eanckbw91#

你可以使用std::tuple_sizestd::tuple_element来获取每个元组元素的类型(这将使它处于相反的顺序,但稍作修改你可以恢复顺序):

#include <iostream>
#include <variant>
#include <vector>

using namespace std;

using Controllers = tuple<int, char, float>;
using ControllersContainer = vector<variant<int, char, float>>;

template <size_t N>
void add(ControllersContainer& ctrls)
{
    ctrls.emplace_back(tuple_element_t<N-1, Controllers>{});
    add<N - 1>(ctrls);
}

template <>
void add<0>(ControllersContainer& ctrls)
{
    ctrls.emplace_back(tuple_element_t<0, Controllers>{});
}

int main()
{
    ControllersContainer ctrls;
    add<tuple_size_v<Controllers>>(ctrls);
}
wwtsj6pe

wwtsj6pe2#

如果允许使用C++20,有一种更方便的方法来解决这个问题:

template<typename T, typename FUNC>
static inline constexpr void for_each_type(FUNC&& func)
{
    auto  __hander = []<typename T, typename FUNC, size_t... I>(FUNC && func, std::index_sequence<I...>) {
        (func.template operator() < std::tuple_element_t<I, T> > (), ...);
    };
    __hander.template operator() < T > (
        std::forward<FUNC>(func),
        std::make_index_sequence<std::tuple_size<T>::value>{}
    );
}

现在你有了两个具有相同方法的类,并将它们变成一个元组:

class A
{
public:
    static const char* name() { return "A"; };
};

class B
{
public:
    static const char* name() { return "B"; };
};
using AB = std::tuple<A, B>;

然后:

int main(int argc, char* argv[])
{
    for_each_type<AB>(
        []<typename T>()
        {
            std::cout << T::name() << std::endl;
        }
    );

    return 0;
}

相关问题