如何改进此模式所需的模板递归深度

How to improve the template recursion depth required for this pattern?

本文关键字:递归 深度 何改进 模式      更新时间:2023-10-16

我一直在我的代码中使用这个问题中描述的模式来制作各种编译时注册列表:c++类型在编译时注册技巧

例如,如果你有一堆lua回调函数,你不想忘记用一些lua状态注册它们,你可以使用一个宏来声明它们,该宏将模板类型放入一个列表中,该列表知道它们的名称和函数指针,然后你有一个一行代码来注册所有的函数。

该技术的一个限制(如SO答案中所述)是,如果列表中有n项,则需要模板递归深度O(n)来求值。这是不理想的,我实际上有相当多的lua回调函数已经…

我曾经认为O(n)递归深度由于各种原因是不可避免的,然而,正如我最近从Yakk那里了解到的,一些我天真地认为需要O(n)的基本事情实际上可以在O(log n)深度中完成。Switch语句可变模板扩展

特别是,所涉及的数据结构不再需要O(log n)模板深度来进行操作。

我不确定的部分是Rank技巧。从引用的代码中,这个模板

template <int N>
struct Rank : Rank<N - 1> {};
template <>
struct Rank<0> {};

是一个关键因素。但是,它在模板深度方面是昂贵的——实例化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) ?

可以为函数重载(?)使用更多精心选择的参数

避免出现以下错误:

模板实例化深度超过900的最大值(使用-ftemplate-depth=增加最大值)实例化'struct Rank<1148u>'

不增加全局最大模板深度,您可以实例化中间模板:

// 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.