9

I'm trying to figure out whether GCC or Clang interpret the C++17 standard differently / wrong here.

This is my code, which does compile using GCC 8, but not using Clang 6:

struct BoolHolder {
    constexpr static bool b = true;
};

template<bool b>
class Foo {};

int main() {
    BoolHolder b;
    Foo<b.b> f; // Works

    BoolHolder & br = b;
    Foo<br.b> f2; // Doesn't work
}

I wonder why that is. Obviously, b.b is a valid constexpr (or the first Foo<b.b> wouldn't be valid). Is br.b not a valid constexpr? Why? The object or the reference itself should have nothing to do with it, since we're accessing a static constexpr member here, right?

If this is really not valid C++17, should the fact that GCC doesn't even warn me (even though I enabled -Wall -Wextra -pedantic) be considered a bug?

Lukas Barth
  • 2,734
  • 18
  • 43
  • FWIW, MSVS and icc also compile. – NathanOliver Aug 06 '18 at 14:54
  • I think this is related to the question: with `constexpr int f(int) { return 0;}`, would `f(x)` be a `constexpr` for `int x;`? https://stackoverflow.com/questions/47696686/generic-lambda-and-its-argument-as-constant-expression – Evg Aug 06 '18 at 15:00
  • I would not bet that the reference is *not* relevant here. `br`is *not* constexpr and so everything based on `br` is also not constexpr. Maybe as workaround you could use `BoolHolder::b`, if `BoolHolder` is not constant within this context you might use `decltype`. – Marcel Aug 06 '18 at 15:03

1 Answers1

10

Clang is correct. References are evaluated "eagerly" in constant expressions, so to speak. [expr.const]/2.11:

An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:

  • [...]
  • an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
    • it is initialized with a constant expression or
    • its lifetime began within the evaluation of e;
  • [...]
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Thanks, but shouldn't the `… unless […] it is initialized with a constant expression …` part be true here? Also, should the expression `br.b` really evaluate to an `id-expression`? It's a static member access, thus the object itself is irrelevant? – Lukas Barth Aug 06 '18 at 15:00
  • `b` is not a constant expression. The object expression is still evaluated even though the result is discarded. – T.C. Aug 06 '18 at 15:24
  • Sad that it is not interpreted as `BoolHolder::b` first. – Jarod42 Aug 06 '18 at 23:03
  • 1
    @Jarod42 Would you prefer `f().x` to not evaluate `f()` if `x` is a static data member? – T.C. Aug 06 '18 at 23:04
  • What does "unless the reference has a preceding initialization" mean? – David G Aug 06 '18 at 23:18