9
#include <exception>

constexpr bool foo(bool x)
{
  return x ? true : throw std::exception();
}

int main()
{
  // 1) must never be compiled
  // static_assert(foo(false), "");

  // 2) must always be compiled?
  const bool x = foo(false);

  // 3) must never compile?
  constexpr bool y = foo(false);

  return 0;
}

我确信 (1) 一定会导致编译错误。我很确定(2)不能在编译时被拒绝,尽管它会在运行时失败。

有趣的例子是 constexpr 变量 (3)。在这个简单的例子中,gcc 和 clang 实际上计算表达式,因此会拒绝程序。(错误消息:y 不是常量表达式)。

是否每个 C++11 编译器都被迫拒绝该程序?如果 foo(false) 被更复杂的表达式代替怎么办?

我很惊讶地发现 constexpr 不是图灵完备的,尽管这将是在规范更改之后: 基于 constexpr 的计算图灵完备吗?

也许这与我的问题有关。据我了解,允许编译器将本示例中 constexpr (3) 的实际评估推迟到运行时。但是如果 constexpr 是图灵完备的,我很难相信编译器可以为所有 constexpr 决定是否会抛出异常(这意味着 constexpr 无效)。

4

2 回答 2

8

根据我的阅读,是的,每个编译器都必须抱怨语句 (3)。

N3242 7.1.5 第 9 段:

对象声明中使用的constexpr说明符将对象声明为const. 这样的对象应具有文字类型并应被初始化。如果它由构造函数调用初始化,则该调用应为常量表达式(5.19)。否则,出现在其初始化程序中的每个完整表达式都应是一个常量表达式。用于转换初始化表达式的每个隐式转换和用于初始化的每个构造函数调用都应是常量表达式(5.19)中允许的那些之一。

我认为一个constexpr对象是“在编译时评估的”,而一个constexpr函数或constexpr构造函数是“可能在编译时评估的”。编译器必须在编译时确定 (3) 等语句的语义有效性。您可能会争辩说“评估”仍然可以在运行时完成,但检查有效性无论如何都会完成大部分工作。另外,代码可以继续实例化模板Check<y>,这几乎可以保证编译器需要y在编译时计算出 的值。

这确实意味着您可以编写一个恶魔般的程序来使编译器花费非常长或无限的时间。但我怀疑这已经可以通过operator->技巧来实现。

于 2012-10-27T21:49:55.503 回答
5

我确信 (1) 一定会导致编译错误。我很确定(2)不能在编译时被拒绝,尽管它会在运行时失败。

正确的。条件运算符的throw部分不是常量表达式,在 (1) 中,它不是未求值的。对于 (2),foo不强制在编译时进行评估。

对于 (3),如何允许编译器推迟评估?constexprdecl-specifier强制foo编译时进行评估。与(1)基本相同,初始化y需要常量表达式的上下文。

§7.1.6 [dcl.constexpr] p9

对象声明中使用的constexpr说明符将对象声明为const. 这样的对象应具有文字类型并应被初始化。如果它由构造函数调用初始化,则该调用应为常量表达式(5.19)。否则,或者如果constexpr在引用声明中使用了说明符,则出现在其初始化程序中的每个完整表达式都应为常量表达式。[...]

于 2012-10-27T21:51:54.910 回答