5

Look at this little example:

constinit int a = 0;
constexpr int b = a;

clang doesn't compile it (godbolt):

2:15: error: constexpr variable 'b' must be initialized by a constant expression

Is this correct diagnostics?

If yes, why doesn't the standard allow this? I understand, that a's value may change during running (or even during dynamic-initialization), but at constant-initialization, its value is known, so it could be used to initialize b.

7
  • 3
    If you allow that, do you also allow this? Would that astonish readers? Commented Oct 2, 2019 at 9:32
  • @chris: yeah, that's a good one. But a different one, as in my example, b is global. We already have differing rules for globals, so maybe the standard could allow this, if b is global. Commented Oct 2, 2019 at 9:35
  • 1
    My comment on that is that every special case in the language comes with its own complexity. Perhaps this case was considered and there wasn't enough motivation to support it, or perhaps it wasn't considered at all. That said, it would be interesting to see a summary of such an in-meeting discussion. Commented Oct 2, 2019 at 9:41
  • 1
    It is the same as void foo(){ int a = 0; constexpr int b = a; }, a is not a constant expression. Commented Oct 2, 2019 at 9:43
  • 1
    So you want to a to be treated as a constant expression depending on how it is initialized and on the context it is being used in? C++ already suffers a lot from context-sensitivity. Commented Oct 2, 2019 at 9:47

3 Answers 3

4

Yes, the diagnostic is correct. constexpr variables must be initialized with a constant expression, and a is not a constant expression (it's a mutable variable).

The purpose of constinit (P1143) is to force a variable declaration to be ill-formed if it's initialization is not constant. It doesn't change anything about the variable itself, like it's type or anything (in the way that constexpr is implicitly const). Silly example:

struct T {
    int i;
    constexpr T(int i) : i(i) { }
    T(char c) : i(c) { }
};

constinit T c(42); // ok
constinit T d('X'); // ill-formed

That is all constinit is for, and the only real rule is [dcl.constinit]/2:

If a variable declared with the constinit specifier has dynamic initialization ([basic.start.dynamic]), the program is ill-formed. [ Note: The constinit specifier ensures that the variable is initialized during static initialization ([basic.start.static]). — end note ]

The const in constinit refers only to the initialization, not the variable, not any types. Note that it also doesn't change the kind of initialization performed, it merely diagnoses if the wrong kind is performed.

In:

constinit int a = 0;
constexpr int b = a;

0 is a constant expression, so the initialization of a is well-formed. Once we get past that, the specifier doesn't do anything. It's equivalent to:

int a = 0; // same behavior, a undergoes constant initialization
constexpr int b = a;

Which is straightforwardly ill-formed.


but at constant-initialization, its value is known, so it could be used to initialize b.

Sure, at this moment. What about:

constinit int a = 0;
cin >> a;
constexpr int b = a;

That's obviously not going to fly. Allowing this would require extending what a constant expression is (already the most complex rule in the standard, in my opinion) to allow for non-constant variables but only immediately after initialization? The complexity doesn't seem worth it, since you can always write:

constexpr int initializer = 0;
constinit int a = initializer;
constexpr int b = initializer;
Sign up to request clarification or add additional context in comments.

8 Comments

This is not entirely true - constinit const can be used in a constexpr - and it's supposed to be the same as constexpr.
@darune What is not entirely true? Yes, because const int, if constant initialized, can be used as a constant expression. That has nothing to do with constinit.
"would require extending what a constant expression is". As I see, yes. It would be just another bullet point: "during static-initialization, constinit equals to constexpr". But I agree, it doesn't worth it. Thanks for suggesting a reasonable workaround.
@geza That bullet point wouldn't work at all. The cin >> a could appear in a static initialization context. It just asks leads to more questions.
@Barry: how? Doesn't cin >> a require dynamic initialization context?
|
2

constexpr combines constinit and const without exception.

constinit forces initialization with a compiletime constant expression, and during static initialization, disallowing dynamic initialization. It does not change the variable itself in any way though.

const forbids changing the variable, though can be weakened by mutable members.

Both together make it a compiletime constant expression.

In summary, yes, the diagnostic is right.

Comments

1

is this correct diagnostics?

I would say yes. According to cppreference:

constinit - specifies that a variable must have static initialization, i.e. zero initialization and constant initialization, otherwise the program is ill-formed.

Static (constant) initialization and constant expression are different concepts in that a constant expression may be used in a constant initialization but not the other way around. constinit shouldn't be confused with const. It means the initialization (only) is constant.

However, constinit const can be used in a constexpr and they are supposed to be the same.

Counter-example:

constinit int a = 0;

struct X{
    X() {
        a = 4;
    }
};

X x{};

constexpr int b = a;

What is b supposed to be ? The point is that a can be changed in non-const ways before b is seen.

2 Comments

... as it is no form of const at all.
@Deduplicator: right. But it just needs one little sentence: "during static initialization, constinit variables are handled as if they were constexpr".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.