在这个问题上,Howard Hinnant 说
std::tuple 的一些实现使用递归继承。但好人没有。;-)
有人可以解释一下吗?
非递归实现具有更好的编译时性能。信不信由你,在一个使用量很大的库设施中,如std::tuple
,它的实现方式会影响(无论好坏)客户端看到的编译时间。递归实现倾向于产生在递归深度上是线性的编译时间(或者可能更糟)。
这不仅仅影响元组本身的实例化。 std::get<I>(tuple)
例如,一个实现将花费线性量的编译时间,而另一个实现将花费恒定量的编译时间。在处理元组的元组时,这种影响可能会迅速恶化(或不会恶化)。即递归实现可能会导致 O(N^2) 编译时间,而非递归实现仍然是 O(1)。
Fwiw,libc++ 实现按客户端指定的顺序排列对象,但使用编译器的空基类优化工具优化空组件的空间。
我不记得 Andrei Alexandrescu 的 GoingNative 2012 演讲的确切内容,但他谈到了这一点,他提到的要点之一是内存布局。如果我有std::tuple<int, short, char, char>
,它将在内存中 aschar, short, int
并且此布局将(在我的系统上)比布局为 4 个字节多int, short, char
。 R. Martinho Fernandes提醒我,最好的办法是以最小化填充的顺序在内存中排序这些,既不是给定的顺序,也不是相反的顺序。(朴素的继承确实是逆序的)。
如果我写std::tuple<int, char, short, char>
,一个通过朴素继承工作的元组会将它们按顺序排列char, short, int
在内存中,使用 3 个字节的填充,当最佳具有零字节的填充时。(int, short, char, char
或char, char, short, int
)。
假设我是对的,它是关于填充的,然后 R. Martinho Fernandes说“[我的论点] 并不排除以最佳顺序在实际实现中使用递归继承。”,这就是我指定天真的继承的原因不好。
(内存中的顺序并不意味着get<0>
会给出不同的对象,并且 R. Martinho Fernandes 正确地指出该顺序应该对用户不可见。但是,这些是我从 GoingNative 事件中得到的提醒。)
视频位于http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Variadic-Templates-are-Funadic,幻灯片位于http://ecn.channel9.msdn.com/events/GoingNative12/ GN12VariadicTemplatesAreFunadic.pdf。
不使用基类链的一个原因是不涉及构造函数链:参数直接转发给适当的子对象。此外,似乎非递归实现对编译器的压力要小得多,并且创建的 [内部] 符号也少得多。更不用说不使用基类链实际上更容易。