我想迭代元组的类型,而不是它的元素。
想象一下,您有一个通用的基类接口
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,但我不知道如何做到这一点(它们迭代元素,而不是类型)。
您可以使用
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);
}
如果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;
}
这是一个简单但紧凑的解决方案:
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()
。#include <cxxabi.h>
并从那里使用 abi::__cxa_demangle
。 (请参阅https://en.cppreference.com/w/cpp/types/type_info/name,注释部分。)或者您可以做其他任何事情,现在您已经有了
itemType
。