【问题标题】:How exactly is std::make_integer_sequence implemented?std::make_integer_sequence 究竟是如何实现的?
【发布时间】:2014-10-11 22:15:13
【问题描述】:

我正在观看一个 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 就绪的编译器中究竟是如何实现的?

请注意,我问的不是how to implement it efficiently,而是编译器供应商实际上决定如何实现它。

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

【问题讨论】:

  • @KerrekSB 我认为他指的是生成序列的编译时复杂度?
  • 你能提供那个演讲的链接吗?
  • @KerrekSB 我只是在谈论编译器供应商完成的实现(可能是 GCC stdlibc++ 和/或 LLVM stdc++),以及选择该实现的原因。我在想一个真正的标准库实现是否依赖于编译器特定的技巧,或者它是用众所周知的 tmp 方法实现的(就像上面在答案中提到的那样)。我不是在询问对编译器内部的深入了解,如果那是你担心的话。
  • @Manu343726 有演讲视频吗? (抱歉跑题了)
  • @kerrecksb 递归深度和实例化计数都是相当客观的抽象性能度量,如果没有,大 n 的编译时间可能足以衡量(例如,3 万亿 size_t?)先上肚。

标签: c++ template-meta-programming c++14


【解决方案1】:

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

libstdc++ (gcc):

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

libc++ (clang):

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

MSVC (VS14 CTP1):

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

ICC 是not currently documented 提供编译时整数常量支持。


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

【讨论】:

  • O(1) 你的意思是 O(n)?
  • @Kknd 哎呀,是的!谢谢!
  • O(n) 你真的是指 O(n^2)?请注意,每个实例化的大小为 O(n),而您执行 O(n) 实例化,因此天真的方法的总编译时间成本为 O(n^2)。
【解决方案2】:

6 年多后,编译器支持内置函数来快速执行此操作。 Clang 和 MSVC 有 __make_integer_seq。 GCC 有__integer_pack。事实上,STL 实现假设存在这样的内置函数!在这三个编译器之间,似乎只有 clang's/libc++ 对make_integer_sequence 有一个后备实现。

The C++ extensions: type traits section of GCC's manual 通过以下方式描述__integer_pack

__integer_pack(长度)

当用作模板定义中的包扩展模式时,扩展为包含从 0 到长度为 1 的整数的模板参数包。这是为高效实现 std::make_integer_sequence 而提供的。

我在clang手册中没有找到描述__make_integer_seq的部分,但是有this review of the commit that added it to 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's STL&lt;type_traits&gt; 第 34 行中定义了这一点:

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

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

#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

【讨论】:

    猜你喜欢
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    • 1970-01-01
    • 2021-07-08
    • 2013-06-29
    • 2023-03-06
    相关资源
    最近更新 更多