46

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

Output

[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
2
  • The standard doesn't say that it can't throw, so far as I can see Commented Dec 15, 2015 at 3:45
  • It was pointed out that there is a gcc bug report on this, no work-arounds provided. Commented Dec 16, 2015 at 18:01

2 Answers 2

41

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.

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:

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

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

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

    • an asm-definition,

    • a goto statement,

    • a try-block, or

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

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

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 is not allowed in a core constant expression, which is covered in section 5.19 [expr.const] paragraph 2 which says:

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

and includes the following bullet:

  • a throw-expression (15.1).

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

Update

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

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

Sign up to request clarification or add additional context in comments.

Comments

11

As shown by Shafik Yaghmour it's a gcc bug, which is fixed in v6.1

If you are still using the old gcc version you can revert to the c++11 constexpr style:

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

However there is a better workaround, still retaining all of the c++14 constexpr extensions:

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.