投入 constexpr 函数

2021-12-18 00:00:00 gcc c++ constexpr c++14

以下代码在 clang++ 3.7.0 下编译,但被 g++ 5.3.1 拒绝.两者都有 -std=c++14 选项.哪个编译器是正确的?任何人都知道标准中的哪里谈论这个?谢谢.

The following piece of code compiles under clang++ 3.7.0 but is denied by g++ 5.3.1. Both have -std=c++14 option. Which compiler is correct? Anyone knows where in the standard talks about this? Thanks.

#include <stdexcept>
using namespace std;

constexpr int f(int n) {
  if (n <= 0) throw runtime_error("");
  return 1;
}

int main() {
  char k[f(1)];
}

输出

[hidden] g++ -std=c++14 c.cpp 
c.cpp: In function ‘constexpr int f(int)’:
c.cpp:7:1: error: expression ‘<throw-expression>’ is not a constant-expression
 }
 ^
[hidden] clang++ -std=c++14 c.cpp 
[hidden] 
[hidden] g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/5.3.1/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --disable-libgcj --with-isl --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 5.3.1 20151207 (Red Hat 5.3.1-2) (GCC) 
[hidden] 
[hidden] clang++ -v
clang version 3.7.0 (http://llvm.org/git/clang.git 2ddd3734f32e39e793550b282d44fd71736f8d21)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/3.4.6
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/5.3.1
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/5.3.1
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64

推荐答案

clang 是正确的,注意 HEAD 修订版gcc 接受 也接受此代码.这是一个格式良好的 constexpr 函数,只要参数的值允许该函数被评估为核心常量表达式.在您的情况下 1 就是这样一个值.

clang is correct, note the HEAD revision of gcc accepts also accepts this code. This is a well-formed constexpr function, as long as there is value for the argument(s) that allows the function to be evaluated as a core constant expression. In your case 1 is such a value.

这在 C++14 标准草案部分 7.1.5 中包含了 constexpr 说明符 [dcl.constexpr],它告诉我们在 constexpr 函数中允许什么:

This is covered in the draft C++14 standard section 7.1.5 The constexpr specifier [dcl.constexpr] which tells us what is allowed in a constexpr function:

constexpr 函数的定义应满足以下约束:

The definition of a constexpr function shall satisfy the following constraints:

  • 它不应是虚拟的 (10.3);

  • it shall not be virtual (10.3);

它的返回类型应该是一个文字类型;

its return type shall be a literal type;

它的每个参数类型都应该是一个文字类型;

each of its parameter types shall be a literal type;

它的函数体应该是=delete,=default,或者一个不包含的复合语句

its function-body shall be = delete, = default, or a compound-statement that does not contain

  • 一个汇编定义,

  • an asm-definition,

goto 语句,

尝试块,或

非文字类型或静态或线程存储持续时间的变量的定义或不进行初始化.

a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.

throw 没有限制,它还说(强调我的):

no restriction on throw and it also says (emphasis mine):

对于非模板、非默认的 constexpr 函数或非模板、非默认、非继承的函数constexpr 构造函数,如果不存在参数值使得函数或构造函数的调用可能是核心常量表达式 (5.19) 的计算子表达式,程序格式错误;不需要诊断.

For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.

在本段下方,我们有以下示例,与您的示例类似:

and below this paragraph we have the following example, similar to yours:

constexpr int f(bool b)
  { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required

throw 在核心常量表达式中不允许,这在 5.19 部分有介绍 [expr.const] 段落 2 表示:

throw is not allowed in a core constant expression, which is covered in section 5.19 [expr.const] paragraph 2 which says:

条件表达式 e 是核心常量表达式,除非对 e 求值,遵循规则抽象机 (1.9),将评估以下表达式之一

A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions

并包括以下项目符号:

  • 抛出表达式 (15.1).

所以当 n <= 0 时,f 将不能用于核心常量表达式.

and so f would not be usable in a core constant expression when n <= 0.

更新

正如 TemplateRex 指出的那样,有两个 gcc 错误报告:

As TemplateRex points out, there are two gcc bugs reports for this:

  • 在 constexpr 函数中从未执行过的throw"未能编译
  • C++14] throw-expression 不是有效的常量表达式

TemplateRex 还指出这些修复不适用于 5.3.0 并且仅在主干中.不,提供了解决方法.

TemplateRex also notes the fixes are not applied to to 5.3.0 and are only in trunk. No, work arounds are provided.

相关文章