Выбросить функцию 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

Ответы

Ответ 1

clang верен, обратите внимание, что HEAD-версия gcc accepts также принимает этот код. Это хорошо сформированная функция constexpr, если есть значение для аргумента (ов), которое позволяет оценивать функцию как основное константное выражение. В вашем случае 1 такое значение.

Это описано в стандартном разделе проекта С++ 14 7.1.5 Спецификатор constexpr [dcl.constexpr], который сообщает нам, что разрешено в функции constexpr:

Определение функции constexpr должно удовлетворять следующим ограничениям:

  • он не должен быть виртуальным (10.3);

  • его тип возврата должен быть литеральным типом;

  • каждый из его типов параметров должен быть литеральным типом;

  • его тело-функция должно быть = delete, = default или составной оператор, который не содержит

    • определение asm,

    • инструкция goto,

    • блок try или

    • определение переменной нелитерального типа или статической или длительности хранения потоков или для которой инициализация не выполняется.

нет ограничений на throw, и он также говорит (внимание мое):

Для несимвольной, не дефолтной функции constexpr или не-шаблона, не дефолтного, не наследующего constexpr constructor, , если значения аргумента отсутствуют, так что вызов функции или конструктора может быть оцененным подвыражением основного константного выражения (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] paragraph 2, в котором говорится:

Условное выражение e является выражением постоянной константы, если оценка e, следуя правилам абстрактная машина (1.9), оценила бы одно из следующих выражений

и включает следующую марку:

  • выражение throw (15.1).

и поэтому f не будет использоваться в основном постоянном выражении, когда n <= 0.

Обновить

Как указывает TemplateRex, для этого есть два отчета об ошибках gcc:

TemplateRex также отмечает, что исправления не применяются к 5.3.0 и находятся только в соединительной линии. Нет, рабочие места предоставляются.

Ответ 2

Как показал Шафик Ягмур, это ошибка gcc, которая исправлена в v6.1

Если вы все еще используете старую версию gcc, вы можете вернуться к стилю c++11 constexpr:

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;
}