std::make_integer_sequence究竟是如何实现的

How exactly is std::make_integer_sequence implemented?

本文关键字:实现 何实现 integer make sequence 究竟 std      更新时间:2023-10-16

我正在观看C++11/14元编程讲座,其中描述了常见算法和tmp模式的一些有效替代方案。

大部分效率增益来自使用可变参数模板而不是递归遍历,在许多情况下,使用可变参数模板的方法是扩展通过索引技巧或其他std::integer_sequence即时技巧生成的可变参数包。
由于这种效率来自于这样一个事实,即实例化std::integer_sequence,特别是别名std::make_integer_sequence并不是一项昂贵的任务,我想确保当前最先进的C++1y标准库实现足够高效,可以使make_integer_sequence即时化不是一项复杂且耗时/内存的任务。
std::make_integer_sequence在 C++1y 就绪编译器中实际实现的究竟是如何实现的?

请注意,我不是在问如何有效地实现它

,而是在问编译器供应商实际上如何决定实现它。

据我所知,make_sequence的唯一实现是简单的 O(n( 递归方法和聪明的 O(logN( 分而治之。

目前没有一个主要的编译器标准库提供 N3658 编译时整数序列的子 O(n((对数或其他(实现。

libstdc++ (gcc(:

标准 O(n( 实现遍历typedef链。这相当于连接到递归调用返回的列表末尾的 FP 函数。

libc++ (clang(:

O(n( 实现,但有一个有趣的 8 倍展开循环。

MSVC (VS14 CTP1(:

O(n(,使用在整数常量和整数序列上模板化的递归继承,后者用作累加器(在FP意义上(。(请注意,VS14 实现实际上位于 type_traits 标头中,而不是utility 中。

ICC 当前未记录为提供编译时整数常量支持。


在这一点上,担心std::integer_sequence的效率可能不值得;编译时整数序列适合的任何问题都会在所用算法的 big-O 性能影响编译时间之前很久就遇到编译器的限制(在函数和模板参数的数量等方面(。还要考虑的是,如果std::make_integer_sequence在编译中的其他任何地方使用(例如在库模板代码中(,那么编译器将能够重用该调用,因为模板元编程是纯粹的功能。

6+ 年后,编译器支持内置功能来快速完成此操作。Clang和MSVC有__make_integer_seq .海湾合作委员会有__integer_pack .事实上,STL 实现假设存在这样的内置!在这三个编译器之间,似乎只有 clang 的/libc++ 具有make_integer_sequence的回退实现。

GCC 手册的C++扩展:类型特征部分通过以下方式描述了__integer_pack

__integer_pack(长度(

当用作模板定义中的包扩展模式时,将扩展到包含从 0 到 length-1 的整数的模板参数包。这是为了有效实施 std::make_integer_sequence 而提供的。

我在 clang 手册中没有找到描述__make_integer_seq的部分,但是有对将其添加到 clang 的提交的评论。

在与我的GCC 11.1.0副本一起安装的libstdcxx中,这是<utility>(第328行(中的代码,用于make_integer_sequence

/// Alias template make_integer_sequence
template<typename _Tp, _Tp _Num>
using make_integer_sequence
#if __has_builtin(__make_integer_seq)
      = __make_integer_seq<integer_sequence, _Tp, _Num>;
#else
      = integer_sequence<_Tp, __integer_pack(_Num)...>;
#endif

同样,Microsoft的STL在第34行<type_traits>中对此进行了定义:

template <class _Ty, _Ty _Size>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Ty, _Size>;

最后,从第 39 行开始libcxx/include/__utility/integer_sequence.h有一个预处理器条件来检查我们是否需要使用回退:

#if __has_builtin(__make_integer_seq) && !defined(_LIBCPP_TESTING_FALLBACK_MAKE_INTEGER_SEQUENCE)
template <class _Tp, _Tp _Ep>
using __make_integer_sequence _LIBCPP_NODEBUG = __make_integer_seq<integer_sequence, _Tp, _Ep>;
#else
// fallback implementation that uses recursive templates