基于 constexpr 的计算图灵是不是完整?

Posted

技术标签:

【中文标题】基于 constexpr 的计算图灵是不是完整?【英文标题】:Is constexpr-based computation Turing complete?基于 constexpr 的计算图灵是否完整? 【发布时间】:2012-03-01 08:54:48 【问题描述】:

我们知道C++ template metaprogramming is Turing complete,但preprocessor metaprogramming is not。

C++11 为我们提供了一种新的元编程形式:constexpr 函数的计算。这种计算形式是图灵完备的吗?我在想,由于 constexpr 函数中允许使用递归和条件运算符 (?:),所以会这样,但我希望有更多专业知识的人来确认。

【问题讨论】:

【参考方案1】:

tl;dr:C++11 中的 constexpr 不是图灵完备的,因为语言规范中存在错误,但该错误已在标准的后续草案中得到解决,并且 clang 已经实现了修复。

constexpr,按照 ISO C++11 国际标准的规定,不是图灵完备的。草图证明:

每个constexpr 函数f 在特定参数序列a... 上的结果(或非终止)仅由a... 的值决定 可以在常量表达式中构造的每个参数值都必须是文字类型,[basic.types]p10 可以是: 标量类型, 参考, 文字类型的数组,或 类类型 上述每种情况都有一组有限的值。 对于标量、非指针类型,这很简单。 对于要在常量表达式中使用的指针或引用,它必须由地址或引用常量表达式初始化,因此必须引用具有静态存储持续时间的对象,在任何程序中都只有有限的数量. 对于数组,边界必须是一个常量,并且每个成员都必须由一个常量表达式初始化,结果随之而来。 对于类类型,成员的数量是有限的,每个成员都必须是字面量类型,并由常量表达式初始化,结果随之而来。 因此,f 可以接收的可能输入集合a... 是有限的,因此任何有限描述的constexpr 系统都是有限状态机,因此不是图灵完备的。

然而,自从 C++11 标准发布后,情况发生了变化。

Johannes Schaub 对std::max() and std::min() not constexpr 的回答中描述的问题已作为核心问题 1454 报告给 C++ 标准化委员会。在 2012 年 2 月的 WG21 会议上,我们认为这是标准中的缺陷,选择的解决方案包括能够使用指定临时对象的指针或引用成员创建类类型的值。这允许constexpr 函数累积和处理无限量的信息,并且足以使constexpr 评估图灵完备(假设实现支持递归到无限深度)。

为了证明 constexpr 的图灵完备性对于实现核心问题 1454 的建议解决方案的编译器,我为 clang 的测试套件编写了一个图灵机模拟器:

http://llvm.org/svn/llvm-project/cfe/trunk/test/SemaCXX/constexpr-turing.cpp

g++和clang的trunk版本都实现了这个核心问题的解决,但是g++的实现目前无法处理这个代码。

【讨论】:

有趣!如果我理解正确,区别在于程序只能有有限数量的静态存储对象,但它可能有无限数量的临时对象。你能解释一下为什么会这样吗? @HighCommander4 每个静态存储时长的对象都由源代码中的一个声明引入(其中只有有限个数,每个都引入了有限个可单独寻址的对象),而无限递归可以引入无限数量的临时对象。这种观点仅适用于 C++ 抽象机——每个真正的实现最终都会达到某种形式的资源限制,因此仍然有一些有限(但通常是未知的)界限。 多么抽象:-) @RichardSmith:您不需要以某种方式处理模板参数包吗?我的意思是,“f 可以接收的一组可能的输入a...”对我来说显然是无限的:有f(1)f(1,1)f(1,1,1),等等到(可数)无穷大。我并不是说 C++11 constexpr 一定是图灵完备的,我只是说你没有真正得到相反的证明,因为你是从有缺陷的前提开始的。 (除非您缺少的前提是实现定义的参数包的最大大小。) @Quuxplusone 不,常量表达式求值不能触发模板实例化,所以模板在这里不相关。常量表达式求值仅对程序的非(值)相关片段进行。 (交错的模板实例化和常量表达式求值是图灵完备的,但模板实例化本身是图灵完备的。)【参考方案2】:

看看这些。我编译了这些示例,它们在 GCC 4.6 中工作: Compile-time computations、Parsing strings at compile-time - Part I、Parsing strings at compile-time - Part II

【讨论】:

+1 : OMG 现在我看到了他们在 GoingNative 会议小组 O__O 中谈论的 constexpr 带来了哪些新的疯狂/令人敬畏; 字符串解析是翻译时间计算变得美丽的地方。 能够读取字符串文字并不意味着它是图灵完备的(例如,它没有演示如何在无限(非半无限)磁带上写入)。【参考方案3】:

如果我们考虑到真实计算机的限制——例如有限内存和 MAX_INT 的有限值——那么,当然,constexpr(以及整个 C++)不是图灵完备的。

但是如果我们取消这个限制——例如,如果我们将 int 视为一个完全任意的正整数——那么是的,C++ 的 constexpr 部分将是图灵完备的。任何部分递归函数都很容易表达。

0, S(n) = n+1 和选择器 I_n^m(x_1, ..., x_n) = x_m 和叠加显然可以用 constexpr 来表示。

原始递归可以直接完成:

constexpr int h(int x1, ..., int xn, int y) 
  return (xn == 0) ? f(x1, ..., xn) : g(x1, ..., xn, y-1, h(x1, ..., xn, y-1));

对于部分递归,我们需要一个简单的技巧:

constexpr int h(int x1, ... int xn, int y = 0) 
  return (f(x1, ... xn, y) == 0) ? y : h(x1, ..., xn, y+1);

所以我们得到任何部分递归函数作为 constexpr。

【讨论】:

对于模拟数学整数的int 类型,这个答案是正确的。但是,对于只有一组有限的不同值的int 类型,结果不符合。

以上是关于基于 constexpr 的计算图灵是不是完整?的主要内容,如果未能解决你的问题,请参考以下文章

C++constexpr和常量表达式

day02

c++11:为啥静态 constexpr 的类内初始化不是定义?

[C++11]constexpr修饰常量表达式

Constexpr变量不是编译时间值吗?

c_cpp 基于样本的图灵虚拟机c