-2

I'm now learning how to use static_assert. What I learned so far is that the argument of the static_assert has to be constant expression. For the following code, I don't know why the argument reference is not a constant expression: It is declared as const and initialized by integral constant 0:

int main()
{
    const int &reference = 0;
    static_assert(reference);
} 

The error message from gcc states that the reference is not usable in a constant expression:

error: 'reference' is not usable in a constant expression.

Is there a standard rule that prohibits reference from being usable in constant expressions so that it is not a constant expression?

  • 3
    _"What I learned so far is that the argument of the static_assert has to be constant expression."_ and has to be an rvalue, otherwise, an lvalue-to-rvalue is applied to its argument which is not allowed in constant expression contexts unless some conditions are met. – mada Sep 20 '22 at 09:32
  • You would need something like [that](https://godbolt.org/z/KcxcnjY7T). – Jarod42 Sep 20 '22 at 09:35
  • **bound** being the key word here. It's not initialized with 0, it can't be. References have to be bound to objects, so where is that object? `0` does not denote an object, it's just a value. – StoryTeller - Unslander Monica Sep 20 '22 at 09:35
  • @StoryTeller-UnslanderMonica - Is this mean that there's no temporary created? I think no because `reference` is bound to a const-qualified temporary holding value `0` Right? – mada Sep 20 '22 at 09:43
  • @John - There is a temporary, of course. The reference can't be bound to anything otherwise. The temporary is what trips the points you already mentioned in your other comments. I'm not sure about the temporary being "const qualified", necessarily. Temporaries in general are mutable by default (but that's beside the point here). – StoryTeller - Unslander Monica Sep 20 '22 at 09:46
  • 1
    Now what you left to check if the variable is usable in constant expression – Language Lawyer Sep 20 '22 at 10:20
  • @LanguageLawyer - Is [expr.const#4.7](https://timsong-cpp.github.io/cppwp/n4868/expr.const#4.7) satisfied here? – mada Sep 20 '22 at 10:23
  • @John See https://timsong-cpp.github.io/cppwp/n4868/expr.const#def:usable_in_constant_expressions (https://timsong-cpp.github.io/cppwp/n4868/expr.const#4.1) – Language Lawyer Sep 20 '22 at 10:24
  • 1
    @LanguageLawyer - If you mean that the `reference` needs to be constexpr, the static_assert will be still an error. – mada Sep 20 '22 at 10:27
  • Dupe of [this](https://stackoverflow.com/questions/73781905/why-does-static-assert-constexpr-function-not-work-in-non-template-struct-but-wo) and [this](https://stackoverflow.com/questions/72023209/how-to-get-std-array-size-in-a-constant-expression/72023705#72023705). – Kal Sep 20 '22 at 10:34
  • @LanguageLawyer - Thanks for your reply. Tell me if possible why `reference` is not usable in constant expression. After reading [const.expr](5.8) I realized that (5.8.1) has to be satisfied: But it requires that the non-volatile glvalue `reference` to refer to an object usable in constant expression, but `reference` refer to a const-qualified temporary object, so, Is this temporary usable in constant expressions? –  Sep 20 '22 at 10:37
  • Dupe/Related: [Why are only some consts useable in compile time array sizes?](https://stackoverflow.com/questions/72597798/why-are-only-some-consts-useable-in-compile-time-array-sizes). – Jason Sep 20 '22 at 10:57

1 Answers1

1

First of all, because you use static_assert(reference); instead of static_assert(!reference); the program is ill-formed whether or not the operand of static_assert is a constant expression, as the assertion will fail. The standard only requires some diagnostic for an ill-formed program. It doesn't need to describe the cause correctly. So for the following I will assume you meant static_assert(!reference);.

The initialization of reference is not a constant expression because the glvalue to which it would be initialized refers to an object which does not have static storage duration (it is a temporary bound to a reference with automatic storage duration), in violation of [expr.const]/11. (At least it seems to be the intention that a temporary lifetime-extended through a reference has the storage duration of the reference. This is not clearly specified though, see e.g. https://github.com/cplusplus/draft/issues/4840 and linked CWG issues.)

Therefore the reference is not constant-initialized (by [expr.cons]/2.2) and it is also not constexpr, so that the reference is not usable in constant expressions (by [expr.const]/4), which is however required of a reference which is referred to by an id-expression in a constant expression if its lifetime didn't start during the constant expression's evaluation (by [expr.const]/5.12). Declaring the reference constexpr only shifts the issue to the declaration failing for the same reason as above.

If you move the declaration of reference to namespace scope it will work (at least after resolution of CWG 2126 and CWG 2481).

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • @foobar No, it refers to the reference. – user17732522 Sep 20 '22 at 11:03
  • After I have read the quotes, (5.8) is checked here: then (5.8.1) is tried: the non-volatile glvalue `reference` refers to an object (in this case the temporary object) that's usable in constant expression: the temporary is usable in constant expressions because it's const-qualified and constant-initialized. So why this exception (5.8.1) is not satisfied here? –  Sep 20 '22 at 11:10
  • @foobar 5.8 doesn't matter. 5.12. applies because `reference` is a reference. – user17732522 Sep 20 '22 at 11:11
  • As far as I can tell, `(5.8)` is tried before `(5.12)` and because lvalue-to-rvalue is evaluated on the static_assert's argument `reference` –  Sep 20 '22 at 11:12
  • This less permissive behavior for reference variables is going to be changed for C++23 by the way, but I would need to have a look at the changes again to see whether it will make this well-formed. – user17732522 Sep 20 '22 at 11:13
  • @foobar The whole enumeration begins with "_An expression E is a core constant expression unless [...] one of the following:_". The order doesn't matter. If one of the items applies it is not a core constant expression. – user17732522 Sep 20 '22 at 11:14
  • _"but I would need to have a look at the changes again to see whether it will make this well-formed."_ Thanks alot for this. Belive me I spend more time to understand this point. And I think that you will see that (5.8) has to be checked because after all an lvalue-to-rvalue is involved so the exceptions (5.8.1) and (5.8.2) are tried. –  Sep 20 '22 at 11:17
  • @foobar I was referring to the C++23 changes which I am not discussing in the answer. Yes, 5.8. must also be checked, but if a later item already applies, then it is redundant to check the prior ones. If any one of the items applies to the expression, then it is not a core constant expression. – user17732522 Sep 20 '22 at 11:19
  • Indeed (5.8.2) is irrelevant - but (5.8.1) is not because I suspect that the glvalue `reference` refer to a (temporary) object _and_ this temporary is usable in constant expression - so I see that this exception has to match. –  Sep 20 '22 at 11:21
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248195/discussion-between-foobar-and-user17732522). –  Sep 20 '22 at 11:23