【问题标题】:How to improve the template recursion depth required for this pattern?如何提高此模式所需的模板递归深度?
【发布时间】:2015-08-27 05:47:19
【问题描述】:

我一直在我的代码中使用此 SO 问题中描述的模式来制作各种编译时注册列表:C++ type registration at compile time trick

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

此技术的一个限制(如 SO 答案中所述)是,如果列表中有 n 项目,则需要模板递归深度 O(n) 来评估。这不太理想,我其实已经有不少lua回调函数了……

我曾认为 O(n) 递归深度由于各种原因是不可避免的,但是正如我最近从 Yakk 在这个(不相关的)答案中了解到的那样,我天真地认为需要 O(n) 的一些基本事情实际上可以在 @ 中完成987654327@ 深度。 Switch statement variadic template expansion

尤其是,没有理由再要求所涉及的数据结构的操作需要O(log n) 模板深度。

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

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

template <>
struct Rank<0> {};

是一个关键成分。但是,就模板深度而言,它的成本很高——实例化Rank&lt;N&gt; 需要模板深度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&lt;100&gt;{})) 的类型总是等于最近定义的重载的返回值。 IE。这些断言通过了

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)

也许为函数重载使用更多、仔细选择的参数 (?)

【问题讨论】:

  • 测量拍摄的内容了吗?作为总时间的一部分,它对您重要吗?还是出于“学术”兴趣?
  • @TonyD:我真正关心的不是时间,而是达到最大模板递归深度(通常为 256)和编译过程出错。
  • 我在一些地方使用了这种模式——如果限制相当大就好了,比如数百万而不是数百,所以我不必跟踪这些东西。除了“学术”兴趣之外,获得这一点比获得O(log n) 更重要。
  • 这项工作的关键在于Rank&lt;M&gt; 是所有M &lt; NRank&lt;N&gt; 的直接或间接基类。你无法真正避免线性实例化。
  • @ChrisBeck 我提到了运行时性能,因为你表面上会不遗余力地使用编译时技术,而不是让你的宏使用注册单个函数的构造函数创建一些静态对象,或者添加用于 post-main() 注册的标准库容器的名称和函数指针。您调用哪种注册函数可以更好地处理编译时(函数指针)类型信息?也许如果我们能看到你最终调用的 lua 注册码,我们可以欣赏/评估需求或好处....

标签: c++ templates c++11 recursion c++14


【解决方案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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-11
    • 2014-05-13
    • 1970-01-01
    • 2014-06-15
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多