8

Consider the following example (snippet (0)):

struct X
{
    constexpr int get() const { return 0; }
};

void foo(const X& x)
{
    constexpr int i = x.get();
}

int main()
{
    foo(X{});
}

The above example compiles with all versions of g++ prior to g++ 10.x, and never compiled under clang++. The error message is:

error: 'x' is not a constant expression
    8 |     constexpr int i = x.get();
      |

live example on godbolt.org

The error kind of makes sense, as x is never a constant expression in the body of foo, however:

  • X::get() is marked constexpr and it does not depend on the state of x;

  • Changing const X& to const X makes the code compile with every compiler (on godbolt.org) snippet (1).


It gets even more interesting when I mark X::get() as static ((on godbolt.org) snippet (2)). With that change, all tested versions of g++ (including trunk) compile, while clang++ still always fail to compile.

So, my questions:

  • Is g++ 9.x correct in accepting snippet (0)?

  • Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?

  • Are g++ 9.x and g++ trunk correct in accepting snippet (2)?

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • constexpr is a 'mask' to a value and a const is a variable that you inform it won't be changed. Values can't be referenced or pointed but const does. The get() function member is processed in compile time. In other words it is 0 to all cases. As I said before, values can't be referenced or pointed – TheArchitect Feb 28 '20 at 15:41
  • 3
    I think [\[expr.const\]/2.11](https://timsong-cpp.github.io/cppwp/n4659/expr.const#2.11) applies here, and `x` in `foo` is not a constant expression. There's even [an old (informally rejected) bug report on clang](https://bugs.llvm.org/show_bug.cgi?id=25693) for its correct behaviour (whereas GCC had an actual bug for it). – dfrib Feb 28 '20 at 15:51
  • This question may be a duplicate of [Invalid explicitly-specified argument in clang but successful compilation in gcc — who's wrong?](https://stackoverflow.com/questions/33872039) or [Why is a constexpr function on a reference not constexpr?](https://stackoverflow.com/questions/54124899). – dfrib Feb 28 '20 at 15:58
  • 3
    Wrote about this recently, seems relevant: https://brevzin.github.io/c++/2020/02/05/constexpr-array-size/ – Barry Feb 28 '20 at 15:59
  • @Barry nice, in your article you also have the reference to the corresponding [GCC bug report](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66477) (I could onloy find the the rejected clang one) which allowed gcc 9.x to accept snippet 0, which has since corrected. – dfrib Feb 28 '20 at 16:08

1 Answers1

12

Is g++ 9.x correct in accepting snippet (0)?

No.

Are all compilers correct in accepting snippet (1)? If so, why is the reference significant?

Yes, they are.

A constant expression cannot use an id-expression naming a reference that doesn't have a previous constant expression initialization or began its lifetime during the constant expression evaluation. [expr.const]/2.11 (same in C++20)

The same is not true if you are naming a non-reference variable without involving any lvalue-to-rvalue conversion. x.get() only refers to x as lvalue and only calls a constexpr function that doesn't actually access any member of x, so there is no issue.

Are g++ 9.x and g++ trunk correct in accepting snippet (2)?

No, because the expression still contains the subexpression x which violates the rule mentioned above.

walnut
  • 21,629
  • 4
  • 23
  • 59
  • [Relevant (informally rejected) bug report on clang](https://bugs.llvm.org/show_bug.cgi?id=25693) pointing out the same correctness in clang (always) failing to compile snippet 0. – dfrib Feb 28 '20 at 15:54