Я столкнулся со сценарием, в котором у меня есть список классов в списке вариативных шаблонов, и, учитывая тип (Target_
), я хочу найти класс в списке (ContainingClass_
), который typedef
s Target_
как ContainingClass_::Class
.
Вот моя текущая реализация грубой силы:
#include <iostream>
#include <cstdlib>
#include <type_traits>
#include <tuple>
template <uint32_t ID_, class Class_>
struct ContainingClass
{
using Class = Class_;
static constexpr uint32_t id() { return ID_; }
};
// Get the index of a type in a type list, or -1 on error (So that we only get the important static_assert).
template <typename Target_, typename ...List_>
struct IndexOf : std::integral_constant<int, -1> {};
template <typename Target_, typename NotTarget_, typename ...List_>
struct IndexOf<Target_, NotTarget_, List_...> : std::integral_constant<std::size_t, 1 + IndexOf<Target_, List_...>::value> {};
template <typename Target_, typename ...List_>
struct IndexOf<Target_, Target_, List_...> : std::integral_constant<std::size_t, 0> {};
// Check if a type exists in a typelist.
template <typename Target_, typename... List_>
struct TypeExists;
template <typename Target_>
struct TypeExists<Target_> : std::false_type {};
template <typename Target_, typename... List_>
struct TypeExists<Target_, Target_, List_...> : std::true_type {};
template <typename Target_, typename NotTarget_, typename... List_>
struct TypeExists<Target_, NotTarget_, List_...> : TypeExists<Target_, List_...> {};
// **THE META-FUNCTION THAT THE QUESTION IS ABOUT**
// Find the ContaingClass that typedefs Target_ as "Class" inside of it.
template <class Target_, class ...ContainingClasses_>
struct ContainingClassFinder
{
static_assert(TypeExists<Target_, typename ContainingClasses_::Class...>::value, "No ContainingClass found for Target_.");
using ContainingClass = typename std::tuple_element<IndexOf<Target_, typename ContainingClasses_::Class...>::value,
std::tuple<ContainingClasses_...>>::type;
};
using namespace std;
// Use the meta function to return the id of the ContainingClass that contains a type.
template <class Target_, class ...ContainingClasses_>
uint32_t get_containing_id(ContainingClasses_...)
{
return ContainingClassFinder<Target_, ContainingClasses_...>::ContainingClass::id();
}
struct Foo {};
struct Bar {};
struct Test {};
struct NonExistent {};
int main()
{
// Prove that the right class was found be printing its ID out.
// Prints 2.
cout << get_containing_id<Test>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
// Causes static_assert.
//cout << get_containing_id<NonExistent>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
return EXIT_SUCCESS;
}
Проблема в том, что он зависит от std::tuple
и выполняет два линейных поиска: один для проверки существования (используя первую вспомогательную метафункцию) и один для получения типа (используя std::tuple_element
).
В идеале я хотел бы получить все это за один раз, без необходимости использования двух вспомогательных метафункций и std::tuple
; это практично? Если нет, любые улучшения в моей реализации будут оценены.
ПРИМЕЧАНИЕ:
<сильный>1. Эта метафункция будет использоваться как деталь реализации библиотеки; Ускорение не вариант.
<сильный>2. Результатом метафункции должен быть тип, в частности тип содержащего его типа, в котором был найден целевой тип. Получение id() класса — это простой способ показать, что метафункция работает.
<сильный>3. Под метафункцией я имею в виду шаблонную структуру, подобную той, что определена в <type_traits>
.