在 C++ 中迭代元组类型

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

我想迭代元组的类型,而不是它的元素。
想象一下,您有一个通用的基类接口

Controller
,并且您想要一个指针向量(我使用原始指针而不是智能指针,以提高可读性):
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,但我不知道如何做到这一点(它们迭代元素,而不是类型)。

c++ templates tuples std template-meta-programming
3个回答
2
投票

您可以使用

std::tuple_size
std::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);
}

0
投票

如果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;
}

0
投票

这是一个简单但紧凑的解决方案:

template<typename TUP, std::size_t Idx = 0>
void printTupElemTypes (TUP tup) {
    if constexpr (Idx < std::tuple_size<TUP>::value) {
        using itemType = typename std::remove_reference<std::tuple_element_t<Idx,TUP>>::type;
        //
        const char *mangledTypeName = typeid(itemType).name();
        printf("Mangled type name of %luth item:  %s\n", Idx, mangledTypeName);
        //
        printTupElemTypes<TUP, Idx + 1>(tup);
    }
}

要调用名为

std::tuple
myTuple
对象或引用,只需

    printTupElemTypes(myTuple);

如果您的目标是打印相应类型的名称(如本例所示),请注意:

  • 需要
    #include <typeinfo>
    ,为了
    typeid()
  • 要获得人类可读的类型名称,需要进行分解;例如,使用 GCC 可以
    #include <cxxabi.h>
    并从那里使用
    abi::__cxa_demangle
    。 (请参阅https://en.cppreference.com/w/cpp/types/type_info/name注释部分。)

或者您可以做其他任何事情,现在您已经有了

itemType

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