2

Cppreference states that the expression a defined in

static const int a = std::random_device{}();

is a glvalue constant expression. That would mean that it must also be a core constant expression that is, among other conditions, its evaluation must not involve any calls to functions (or constructors) that aren't declared constexpr as stated here (3rd point). Looking at std::random_device constructors, I'm not seeing any constructor being constexpr.

I checked out the draft on this and I'm not seeing any explicit mention of constructors, but I would say it is implied here:

— an invocation of a non-constexpr function;

Am I missing something here? On the other hand, why isn't it a prvalue constant expression?

domdrag
  • 541
  • 1
  • 4
  • 13
  • 1
    Glvalue in essence means the address. `&a` is indeed a constant expression. On the other hand, doing lvalue to rvalue conversion on `a` results in a prvalue that clearly isn't constant, the reason being its initialization isn't. – Passer By Dec 14 '22 at 07:30
  • @PasserBy Yes, that makes sense, but I was looking for a more standard-point-of-view answer. Also, nothing is stated about `&a` expression, only the sole `a`. – domdrag Dec 14 '22 at 08:29
  • The example you refer to is `static const int a = std::random_device{}(); constexpr const int& ra = a; /* OK: a is a glvalue constant expression */ constexpr int ia = a; /* Error: a is not a prvalue constant expression` */ - `a` itself is not constant expression but referense/pointer to `a` is because it is static storage duration. – dewaffled Dec 14 '22 at 10:15
  • @dewaffled Why does it say it is a glvalue constant expression though? – domdrag Dec 14 '22 at 10:37
  • `A constant expression is either ... glvalue (since C++14) core constant expression that refers to an object with static storage duration ... [or] a prvalue core constant expression whose value satisfies the following constraints: ...` - `a` refers to an object with static storage duration but `a` value is not prvalue constant expression – dewaffled Dec 14 '22 at 10:53
  • @dewaffled That's the definition of constant expression, I'm asking why is it a core constant expression. – domdrag Dec 14 '22 at 11:02
  • @dewaffled My bad, I didn't put the word core when I asked you the first time. – domdrag Dec 14 '22 at 11:05
  • I don't get it: `glvalue core constant expression ... refers to an object with static storage duration` -`a` is an object with static storage duration and is glvalue core constant expression but its value is not constant expression. That's all what they mean in the example. – dewaffled Dec 14 '22 at 11:12
  • `&a` captures what the glvalue of `a` is. If that doesn't make sense to you, make sure you do understand what value categories are. By the looks of it, you probably don't want the standard quoted at you, it's more confusing then not. – Passer By Dec 14 '22 at 12:09
  • @dewaffled But why is `a` glvalue core constant expression? – domdrag Dec 14 '22 at 12:14
  • @PasserBy Cppreference stated that `a` is glvalue core constant expression. I'm fairly certain that in order to assert that fact you shouldn't use `&a` expression whatsoever. – domdrag Dec 14 '22 at 12:16
  • `static const int a = std::random_device{}();` is not an expression at all. It's a declaration. The expression `std::random_device{}()` used as an initializer in this declaration is not a constant expression. In a declaration `constexpr const int& ra = a;`, the initializer `a` is a core constant expression because it doesn't involve any operations prohibited by **[expr.const]/5**; in particular, it doesn't involve an lvalue-to-rvalue conversion. (continued) – Igor Tandetnik Dec 14 '22 at 13:29
  • In `constexpr int ia = a;`, initializer `a` is not a constant expression because its evaluation involves lvalue-to-rvalue conversion of an object not *usable in a constant expression*, violating **[expr.const]/(5.8)**. In turn, `a` is not *usable in a constant expression* because it's not *constant-initialized* – Igor Tandetnik Dec 14 '22 at 13:33
  • @IgorTandetnik I believe you're right. You should make an answer. – domdrag Dec 14 '22 at 15:41
  • 2
    Although you can tag questions with [language-lawyer], it doesn't guarantee that you'll get the answer that you want. If you don't even seem to have a basic understanding of the principles behind the feature that you're asking about, then it's less likely that people will give you references to the standard. It's very hard to understand the standard unless you sort of already know what it means :) – Brian Bi Dec 15 '22 at 01:12
  • @BrianBi The question is seemingly well-put and unambiguous so it should be known what kind of answer I am looking for. You can go around the SO and give your own opinions on people's "basic understanding" or what-not, but if you don't have anything to contribute about the question, I would advise you to not get involved. – domdrag Dec 15 '22 at 06:36
  • Yes, again, the fact that you make it known what you want doesn't mean you'll get it. If someone asked why the code `int& r = 3;` doesn't compile, tagged the question [language-lawyer], and mentioned in the question some references to the draft that they've seemingly misunderstood, they wouldn't necessarily get an answer that explains the standardese. But they could come back and write a second question asking where in the standard it says non-const references can't bind to prvalues. That's basically what my advice is here. – Brian Bi Dec 15 '22 at 12:42
  • @BrianBi I think you should read the question again, it seems to me you didn't quite understand it. – domdrag Dec 15 '22 at 15:44
  • 1
    I understood the question. I understood that you wanted an explanation that references the standard. However, I'm trying to explain to you why you didn't get the answer you wanted. If someone doesn't even realize the basic fact that the expression `a` doesn't contain the function calls that were used to initialize the variable `a`, people who answer the question are going to worry that their effort in explaining the standardese will be wasted on the person. Again, I would encourage you to add a second question. – Brian Bi Dec 15 '22 at 18:55

1 Answers1

4

Let's make this example simpler (and probably cppreference should do the same):

int f(); // not constexpr, not even defined

void test() {
    static const int a = f();
    constexpr const int& ra = a; // OK: a is a glvalue constant expression
    constexpr int ia = a;        // Error: a is not a prvalue constant expression
}

It's true that f() -- or, in the original example, std::random_device{}() -- is not a constant expression. But we don't need it to be to initialize ra. Because we're not reading a, we're just binding a reference to it -- all we need to bind a constexpr reference to a is for a to have static storage duration, which it does. So that's fine.

But reading a as a constant is not allowed, that's what the next line is indicating: we can't initialize ia because a isn't a prvalue constant expression (specificaly, we're violating the rule that we can't apply an lvalue-to-rvalue conversion becuase a isn't usable in constant expressions, which is currently [expr.const]/5.9).

That's basically the distinction between ra and ia: ra just needs a to have a stable address, because that's the part that we need to be constant. But ia needs a to have a constant value. This might be even more obvious if we did:

void test()
{
    static const int a = f();
    constexpr const int& ra = a;  // OK
    constexpr const int* pa = &a; // OK
    constexpr int ia = a;         // Error
}

ra and pa are equivalent - the only thing we care about is the address of a, not the value of a. The value of a isn't constant, but the address is, so it works.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • This comment is great for someone who doesn't want to be bothered with the exact definitions from the standard. I was looking for a more language-lawyerly answer. You did give exact answer on why it isn't a prvalue constant expression, but the main question still remains unanswered from the standard point of view. I believe Igor Tandetnik got to the point in the comments. The problem was that I mistakenly thought that we should consider evaluation from the definition of `a` when deciding whether `a` is a constant expression (in future uses). I basically interpreted `a` as a function. – domdrag Dec 14 '22 at 17:20
  • 1
    @domdrag I mean, I'm not sure what you're looking for really. Initializing `ra` from `a` works because there's no rule being violated. It's hard to cite the lack of rule being violated. At no point during that initialization are we even considering if _the value of_ `a` is a constant expression, we're just grabbing its address. – Barry Dec 15 '22 at 18:24