7

The following code only compiles on GCC (checked it on 10.4 and 13.2 on godbolt.org) but not on Clang (fails on all versions I tried, for example 17.0.1 on godbolt.org):

struct A { static constexpr int b{1}; }; int main(int argc, char *argv[]) { A a; A& aref{a}; constexpr auto bb1{a.b}; constexpr auto bb2{aref.b}; return bb1+bb2; } 

Clang outputs:

<source>:9:20: error: constexpr variable 'bb2' must be initialized by a constant expression 9 | constexpr auto bb2{aref.b}; | ^ ~~~~~~~~ <source>:9:24: note: initializer of 'aref' is not a constant expression 9 | constexpr auto bb2{aref.b}; | ^ <source>:7:14: note: declared here 7 | A& aref{a}; | 

https://godbolt.org/z/nG4j3KefE

Why?

6
  • 1
    Clang is correct. Basically a duplicate of stackoverflow.com/questions/60454862 Commented Oct 31, 2023 at 12:56
  • Probably, related: stackoverflow.com/questions/54124899/… Commented Oct 31, 2023 at 12:58
  • 3
    Both proposed links above are referring to function argument. Here we do not have a function, but reference to variable and static class field. So issue is different. Also note that A::b is a static field which is referenced by . (dot). Commented Oct 31, 2023 at 13:13
  • 1
    @phinz I'm pretty sure that's an msvc bug then. Commented Oct 31, 2023 at 13:43
  • 1
    Actually, reading through the wording again, the rules about references appear to have changed in C++23: e.g., we can now use reference parameters in constexpr contexts (other rules still have to be followed, but just the fact that it's a reference doesn't automatically prevent constant evaluation). So maybe this code should be valid now. Commented Oct 31, 2023 at 15:00

1 Answer 1

6

bb1 was always permitted.

bb2 is allowed since P2280 which was accepted for C++23 as defect report for earlier revisions as well.

Originally there was a specific rule that in a constant expression forbade any use of a reference variable (but only reference variables!) that wasn't usable in constant expressions (i.e. constexpr initialized with constant expresison) or had its lifetime started during the constant expression evaluation. This held even if no lvalue-to-rvalue conversion was applied and nothing else depended on the specific referenced objects. This was inconsistent with usage of non-reference variables and the defect report fixes that. Your example demonstrates the inconsistency.

GCC technically didn't behave conforming before the defect report and Clang isn't behaving conforming since it, but probably simply hasn't implemented it yet.

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

4 Comments

Could you also please tell me what's the reasoning behind the original rule? Just a design mistake or something else? Especially in this case it seems silly to forbid that, I use references as aliases to shorten nested access.
@phinz It has been there since C++11, might be a bit difficult to find a rationale, but I think at the time the focus of constexpr was to allow very simply by-value functions to replace macros and arithmetic in template metaprogramming. I guess there wasn't a focus on using objects by-reference and as you can see in P2280 there is still need to consider some details when relaxing the rule.
Thank you, I had the vague idea it could have something to do to avoid confusion in polymorphism but this makes more sense.
@phinz As support for my conjecture, they only allowed references in constexpr context at all with open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3078.html relatively late in the drafting of C++11. Before that only value parameters were allowed (and variable declarations in constexpr functions weren't allowed in C++11 anyway).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.