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

Chr*_*eck 7 c++ recursion templates c++11 c++14

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

例如,如果你有一堆lua回调函数,并且你不想忘记用一些lua状态注册它们,你可以使用一个宏来声明它们,这个宏将一个知道它们的名字和函数指针的模板类型放入一个列表中,然后你有一个记录所有功能的单行程.

这种技术的局限性(如SO答案中所述)是,如果n列表中有项目,则需要模板递归深度O(n)进行评估.这不太理想,我实际上已经有相当多的lua回调函数......

我曾经相信O(n)递归深度由于各种原因是不可避免的,但是正如我最近从Yakk那里学到的那样(未经证实的)答案,我天真地想到的一些基本的东西O(n)实际上可以O(log n)深入地完成.切换语句可变参数模板扩展

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

我不确定的部分是Rank诀窍.从引用的代码,这个模板

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

template <>
struct Rank<0> {};
Run Code Online (Sandbox Code Playgroud)

是一个关键因素.虽然在模板深度方面很昂贵 - 实例化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; }
Run Code Online (Sandbox Code Playgroud)

那么在代码中的任何一点,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:");
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点,不需要模板递归深度O(n)?

也许为函数重载使用更多,精心选择的参数(?)

Jar*_*d42 7

为避免类型错误:

模板实例化深度超过最大值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>;
Run Code Online (Sandbox Code Playgroud)

你可能还有一个帮手:

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>
{};
Run Code Online (Sandbox Code Playgroud)

然后

template struct Instantiate<Rank, 2000, 250>; // Rank<2000> is now instantiated.
Run Code Online (Sandbox Code Playgroud)