Как улучшить глубину рекурсии шаблона, необходимую для этого шаблона?
я использую шаблон, описанный в этом вопросе SO в моем коде, чтобы сделать списки регистрации во время компиляции различных видов:Регистрация типа C++ во время компиляции trick
например, если у вас есть куча функций обратного вызова lua, и вы не хотите забывать регистрировать их в некотором состоянии lua, вы можете объявить их с помощью макроса, который помещает тип шаблона, который знает их имя и указатель функции в список, а затем у вас есть один-лайнер, который регистрирует все функции.
ограничение этой техники (как описано в ответе SO) заключается в том, что если у вас есть n
элементы в списке, это требует глубины рекурсии шаблона O(n)
для оценки. Это не идеально, у меня на самом деле уже есть несколько функций обратного вызова lua...
я верил, что O(n)
глубина рекурсии неизбежна по разным причинам, однако, как я недавно узнал от Yakk в этом (unreleated) ответе, некоторые фундаментальные вещи, которые, как я наивно полагал, требовались O(n)
на самом деле можно сделать в O(log n)
глубину. переключатель заявление variadic шаблон расширения
в частности, больше нет причин, по которым задействованные структуры данных должны требовать O(log n)
глубина шаблона для их операций.
часть, в которой я не уверен, это Rank
трюк. Из ссылочного кода этот шаблон
template <int N>
struct Rank : Rank<N - 1> {};
template <>
struct Rank<0> {};
является ключевым ингредиентом. Это дорого в условия глубины шаблона, хотя -- instantiating Rank<N>
требуется глубина шаблона N
. Важным свойством, которое он имеет, является то, что если функция f
определяется, который перегружен с использованием многих различных типов ранга, разрешение перегрузки всегда выбирает перегрузку наибольшего ранга, потому что это "самый производный экземпляр". Е. Г. если у нас есть этот код:
bool f(Rank<0>) { return false; }
int f(Rank<1>) { return 0; }
float f(Rank<2>) { return 0; }
double f(Rank<3>) { return 0; }
тогда это всегда так, что в любой момент кода, decltype(f(Rank<100>{}))
имеет тип, равный возвращение значение последней определенной перегрузки. Т. е. эти утверждения проходят
bool f(Rank<0>) { return false; }
static_assert(std::is_same<bool, decltype(f(Rank<100>{}))>::value, "D:");
int f(Rank<1>) { return 0; }
static_assert(std::is_same<int, decltype(f(Rank<100>{}))>::value, "D:");
float f(Rank<2>) { return 0; }
static_assert(std::is_same<float, decltype(f(Rank<100>{}))>::value, "D:");
double f(Rank<3>) { return 0; }
static_assert(std::is_same<double, decltype(f(Rank<100>{}))>::value, "D:");
есть ли способ сделать это, который не требует глубины рекурсии шаблона O(n)
?
возможно, используя более тщательно выбранные параметры для перегрузки функции (?)
1 ответов
чтобы избежать ошибки типа:
глубина создания экземпляра шаблона превышает максимум 900 (используйте-ftemplate-depth= для увеличения максимума) создание экземпляра 'struct Rank'
не увеличивая глобально максимальную глубину шаблона, вы можете создавать промежуточные шаблоны:
// To allow instantiation of Rank<1000> with template-depth at 256
template struct Rank<250>;
template struct Rank<500>;
template struct Rank<750>;
template struct Rank<1000>;
у вас также может быть помощник:
namespace detail
{
template <template <std::size_t> class C,
typename Seq,
std::size_t BlockSize>
struct Instantiate_Impl;
template <template <std::size_t> class C,
std::size_t... Is,
std::size_t BlockSize>
struct Instantiate_Impl<C, std::index_sequence<Is...>, BlockSize>
{
std::tuple<C<(Is * BlockSize)>...> dummy;
};
}
template <template <std::size_t> class C,
std::size_t N,
std::size_t BlockSize = 250>
struct Instantiate :
detail::Instantiate_Impl<C,
std::make_index_sequence<1 + N / BlockSize>,
BlockSize>
{};
а то
template struct Instantiate<Rank, 2000, 250>; // Rank<2000> is now instantiated.