Как извлечь высокоиндексированную специализацию из структуры?
Я пытаюсь выполнить метапрограммирование нескольких шаблонов, и мне нужно "извлечь" самый высокий индекс специализации некоторой структуры в некотором типе.
Например, если у меня есть некоторые типы:
struct A
{
template<unsigned int> struct D;
template<> struct D<0> { };
};
struct B
{
template<unsigned int> struct D;
template<> struct D<0> { };
template<> struct D<1> { };
};
struct C
{
template<unsigned int> struct D;
template<> struct D<0> { };
template<> struct D<1> { };
template<> struct D<2> { };
};
Как я могу написать метафункцию следующим образом:
template<class T>
struct highest_index
{
typedef ??? type;
// could also be: static size_t const index = ???;
};
чтобы дать мне индекс с наивысшим индексом D
, который был специализирован внутри произвольной структуры, такой как выше, без, требующей, чтобы struct явно объявила счет?
Ответы
Ответ 1
Это первая версия, которая дает вам максимальный индекс, для которого определена специализация. Из этого вы получите соответствующий тип!
Реализация:
template<class T>
struct highest_index
{
private:
template<int i>
struct is_defined {};
template<int i>
static char f(is_defined<sizeof(typename T::template D<i>)> *);
template<int i>
static int f(...);
template<int i>
struct get_index;
template<bool b, int j>
struct next
{
static const int value = get_index<j>::value;
};
template<int j>
struct next<false, j>
{
static const int value = j-2;
};
template<int i>
struct get_index
{
static const bool exists = sizeof(f<i>(0)) == sizeof(char);
static const int value = next<exists, i+1>::value;
};
public:
static const int index = get_index<0>::value;
};
Тестовый код:
#include <iostream>
struct A
{
template<unsigned int> struct D;
};
template<> struct A::D<0> { };
template<> struct A::D<1> { };
struct B
{
template<unsigned int> struct D;
};
template<> struct B::D<0> { };
template<> struct B::D<1> { };
template<> struct B::D<2> { };
int main()
{
std::cout << highest_index<A>::index << std::endl;
std::cout << highest_index<B>::index << std::endl;
}
Вывод:
1
2
Живая демонстрация.: -)
Ответ 2
Выяснил это с помощью комментариев по этому вопросу!
struct A { template<unsigned int> struct D; };
template<> struct A::D<0> { };
struct B { template<unsigned int> struct D; };
template<> struct B::D<0> { };
template<> struct B::D<1> { };
struct C { template<unsigned int> struct D; };
template<> struct C::D<0> { };
template<> struct C::D<1> { };
template<> struct C::D<2> { };
template<> struct C::D<3> { };
template<unsigned int>
static unsigned char test(...);
template<unsigned int N, class T>
static typename enable_if<
sizeof(typename T::template D<N>),
unsigned char (&)[1 + sizeof(test<N + 1>(T()))]
>::type test(T, typename T::template D<N> = typename T::template D<N>());
int main()
{
return sizeof(test<0>(C())) - 1; // Evaluates to number of specializations
}
Ответ 3
Вот мой небольшой вклад.
Начнем с методов существования:
template <unsigned>
static unsigned char exists_impl(...);
template <unsigned N, typename T>
static auto exists_impl(T const&&) ->
typename std::enable_if<sizeof(typename T::template D<N>),
unsigned char (&)[2]>::type;
template <typename T, unsigned N>
static constexpr bool exists() {
return sizeof(exists_impl<N>(std::declval<T>())) != 1;
}
Я считаю, что constexpr
и использование функций очень много приносят в таблицу с точки зрения удобочитаемости, поэтому я не использую типичные типы.
Затем мы используем типичный двоичный поиск (вторая попытка, см. первую попытку внизу), с потерей читаемости, но чтобы воспользоваться ленивым экземпляром, мы используем частичную специализированную специализацию и std::conditional
:
template <typename T, unsigned low, unsigned high, typename = void>
struct highest_index_in;
template <typename T, unsigned low>
struct highest_index_in<T, low, low>: std::integral_constant<unsigned, low> {};
template <typename T, unsigned low, unsigned high>
struct highest_index_in<T, low, high, typename std::enable_if<(high == low + 1)>::type>:
std::integral_constant<unsigned, low + exists<T, low+1>()> {};
template <typename T, unsigned low, unsigned high>
struct highest_index_in<T, low, high, typename std::enable_if<(high > low + 1)>::type>:
std::conditional< exists<T, (low+high)/2>(),
highest_index_in<T, (low+high)/2, high>,
highest_index_in<T, low, (low+high)/2> >::type
{};
template <typename T>
static constexpr unsigned highest_index() {
return highest_index_in<T, 0, ~(0u)>::value;
} // highest_index
Демо на liveworkspace, вычисление highest_index<C>()
почти мгновенно.
Первая попытка двоичного поиска, к сожалению, компилятору необходимо рекурсивно создавать тела функций (чтобы доказать, что они могут быть созданы), и поэтому работа, которую он должен выполнить, огромна:
template <typename T, unsigned low, unsigned high>
static constexpr auto highest_index_in() ->
typename std::enable_if<high >= low, unsigned>::type
{
return low == high ? low :
high == low + 1 ? (exists<T, high>() ? high : low) :
exists<T, (high + low)/2>() ? highest_index_in<T, (high+low)/2, high>() :
highest_index_in<T, low, (high+low)/2>();
} // highest_index_in
template <typename T>
static constexpr unsigned highest_index() {
return highest_index_in<T, 0, ~(0u)>();
} // highest_index
Итак, к сожалению, highest_index
неприменим, а clang dang slow (а не то, что gcc выглядит лучше).