web-dev-qa-db-ja.com

constexpr関数をスローします

次のコードはclang ++ 3.7.0でコンパイルされますが、g ++ 5.3.1では拒否されます。両方持っています -std=c++14オプション。どのコンパイラが正しいですか?誰もが標準のどこでこれについて話すか知っていますか?ありがとう。

#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
29
Kan Li

clangは正しいです gccのHEADリビジョンは受け入れます もこのコードを受け入れます。関数をコア定数式として評価できるようにする引数に値がある限り、これは整形式のconstexpr関数です。あなたの場合、1はそのような値です。

これはドラフトC++ 14標準セクション7.1.5でカバーされていますconstexpr指定子[dcl.constexpr]。

Constexpr関数の定義は、次の制約を満たす必要があります。

  • 仮想であってはならない(10.3)。

  • その戻り型はリテラル型でなければなりません。

  • その各パラメータタイプはリテラルタイプでなければなりません。

  • そのfunction-bodyは、= delete、= default、またはを含まない複合ステートメントでなければなりません。

    • asm-definition、

    • gotoステートメント、

    • トライブロック、または

    • 非リテラル型の変数、静的またはスレッドのストレージ期間の変数の定義、または初期化が実行されない変数の定義。

throwに対する制限はなく、また(emphasis mine)と表示されます:

非テンプレート、非デフォルトのconstexpr関数、または非テンプレート、非デフォルト、非継承のconstexprコンストラクターの場合、関数またはコンストラクターの呼び出しなどの引数値が存在しない場合コア定数式(5.19)の評価された部分式である可能性があります。プログラムの形式が正しくありません;診断は必要ありません。

そしてこの段落の下に、あなたに似た次の例があります。

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は次のように述べています:

条件式eは、抽象マシン(1.9)の規則に従ってeを評価すると、次の式のいずれかが評価されない限り、コア定数式です。

次の箇条書きが含まれています。

  • スロー式(15.1)。

したがって、fは、n <= 0の場合、コア定数式では使用できません。

更新

TemplateRexが指摘するように、これには2つのgccバグレポートがあります。

TemplateRexはまた、修正は5.3.0には適用されず、トランクのみに適用されることにも注意しています。いいえ、回避策は提供されています。

20
Shafik Yaghmour

Shafik Yaghmour で示されているように、v6.1で修正されたgccのバグです。

古いgccバージョンをまだ使用している場合は、c++11constexprスタイルに戻すことができます。

constexpr auto foo(int n) -> int
{
  return n <= 0 ? throw runtime_error("") : 1;
}

ただし、より良い回避策がありますが、c++14 constexpr拡張機能はすべて保持されています。

// or maybe name it
// throw_if_zero_or_less
constexpr auto foo_check_throw(int n) -> void
{  
  n <= 0 ? throw std::runtime_error("") : 0;
}

constexpr auto foo(int n) -> int
{
  foo_check_throw(n);

  // C++14 extensions for constexpr work:
  if (n % 2)
    return 1;
  return 2;
}
7
bolov