C ++中是否有类型的constexpr排序?

时间:2014-07-05 19:40:13

标签: c++ templates binary-search-tree template-meta-programming

C ++是否提供所有类型集的排序作为常量表达式?任何人都会做哪个特定的顺序并不重要。这可以是constexpr比较函数的形式:

template <typename T1, typename T2>
constexpr bool TypeLesser ();

我对此的使用是针对编译时自我平衡类型的二叉搜索树,作为(cons / nil)类型列表的替代,以加速编译。例如,检查类型是否包含在这样的树中可能比检查它是否包含在类型列表中更快。

如果标准C ++没有提供这样的功能,我也会接受特定于编译器的内在函数。

请注意,如果获得排序的唯一方法是通过在整个代码库(包括许多模板和匿名结构)中添加样板来手动定义它,我宁愿留在类型列表中。

2 个答案:

答案 0 :(得分:0)

标准的唯一排序是通过type_info(由typeid表达式提供),您可以通过type_index更轻松地使用 - 后者提供普通的比较功能,以便可以使用在收藏中。

我猜它的祖先是Andrei Alexandrescu在“现代C ++设计”中的类。

这不是编译时间。


为减少编译时间,您可以为相关类型定义特征类,为每个类型指定一些序数值。 128位UUID可以很好地作为类型ID,以避免保证唯一ID的实际问题。当然,这假定您或客户端代码控制可能的类型集。

必须注册&#34;以前在Boost机器中使用相关类型来确定函数结果类型。


无论如何,我必须建议认真测量编译性能。在运行时快速的平衡操作,仅涉及调整几个指针,在编译时可能很慢,涉及创建一个全新类型的巨大描述符。因此,即使检查类型集成员资格可能更快,但构建类型集可能会非常慢,例如, O(名词 2 )。

免责声明:我没有尝试过。

但无论如何,我再次记得Andrei Alexandrescu在已经提到过的“现代C ++设计”中讨论过某种类型的东西,或者如果你无法访问那本书,那就看看Loki库(这是一个那本书里的东西。)

答案 1 :(得分:0)

您有两个主要问题: 1)您没有具体的比较标准(因此问题,不是吗?), 2)您没有在编译时排序的任何标准方法。

第一次使用std::type_info和其他人一样(当前通过std::type_index包装器在地图上使用)或定义自己的元函数来指定不同类型的排序标准。对于第二种,您可以尝试编写自己的基于模板元编程的快速排序算法。这就是我为我的个人元编程库所做的,并且完美无缺。

关于假设“自我平衡搜索树应该比经典类型列表表现更好”我真的鼓励你做一些表演(尝试templight)然后说出来。 编译时性能与经典运行时性能无关,在很大程度上取决于编译器具有的模板即时系统的确切实现
例如,根据我自己的经验,我很确定我的简单“O(n)”线性搜索可以比你的自平衡树表现得更好。为什么?的记忆化即可。编译时性能不仅仅是即时深度。事实上,记忆化在这方面起着至关重要的作用。

给你一个真实的例子:考虑quicksort(伪元代码)的实现:

list sort( List l )
{
    Int pivot = l[l.length/2];

    Tuple(List,List) lists = reorder( l , pivot , l.length/2 );

    return concat( sort( lists.left ) , sort( lists.right ) );
}

我希望这个例子不言自明。注意它的功能方式,没有副作用。如果有一天C ++中的元编程有这种语法,我会很高兴...

这是quicksort的递归案例。由于我们使用的是类型列表(在我的例子中是Variadic类型列表),因此计算pivot的值的第一个metainstruction具有O(n)复杂度。特别要求模板的即时深度为N / 2。秒步(重新排序)可以在O(n)中完成,并且连接是O(1)(记住这是C ++ 11可变参数类型列表)。

现在考虑执行的一个例子:

  

[1,2,3,4,5]

第一步调用递归情况,因此跟踪为:

  • Int pivot = l[l.length/2];遍历列表,直到3.这意味着执行遍历[1][1,2][1,2,3]所需的即时消息。< /强>

  • 在重新排序期间,会生成更多次级遍历(以及元素“交换”生成的次级遍历组合)。

  • 递归“调用”和concat。

由于执行到列表中间的这种线性遍历被记忆,它们仅在孔排序执行时被实例化一次。当我第一次使用templight遇到这个时,我完全傻眼了。查看瞬态图的事实是,只有第一个大型遍历被实例化,小的只是大部分的一部分,因为大的地方被记忆,少数不再实例。 / p>

哇,编译器能够记忆至少那么慢的线性遍历的一半,对吧?但是,这种巨大的备忘录工作的成本是多少?

我想用这个答案说的是:在进行模板元编程时,忘记关于运行时性能,优化,成本等的一切,并且不做假设。衡量。你正在进入一个完全不同的联盟。我不完全确定什么实现(你的selft平衡树与简单的线性遍历)更快,因为这取决于编译器。我的例子只是为了说明编译器实际上可以完全分解你的假设。

旁注:我第一次做这个评分时,我把它们展示给了我大学的算法老师,而他仍在努力弄清楚发生了什么。事实上,他在这里问了一个关于如何衡量这个怪物的复杂性和表现的问题:Best practices for measuring the run-time complexity of a piece of code