8

Since C++20, std::vector can be used in constant expressions. And as far as I known, current C++ permits dynamically allocate memory under the condition that any such allocation is deallocated by the time the constant expression is "over".

However, I encounter that in case of immediate function the rules might be different. Please consider the example:

consteval auto getVec() {
    return std::vector<int>(9);
}

static_assert( getVec().size() == 9 );

Here immediate consteval function getVec returns not-empty std::vector, size of which is verified in a constant expression.

I expected that this code would compile, because all deallocations must be done automatically, and indeed it is accepted in Clang with libc++.

But MSVC complains:

error C7595: 'getVec': call to immediate function is not a constant expression
note: (sub-)object points to memory which was heap allocated during constant evaluation
fatal error C1903: unable to recover from previous error(s); stopping compilation

and GCC behaves similarly:

error: 'getVec()()' is not a constant expression because it refers to a result of 'operator new'

Online demo: https://gcc.godbolt.org/z/d736qr3hh

Which compiler is correct here?

wohlstad
  • 12,661
  • 10
  • 26
  • 39
Fedor
  • 17,146
  • 13
  • 40
  • 131
  • 2
    It will be valid in C++23 because the rules changed so that a call to a `consteval` function inside a manifestly-constant evaluated expression is no longer by itself an immediate invocation, which seems to be what you expect of C++20. – user17732522 Jun 11 '23 at 15:11
  • @user17732522, thanks. It seems that no compiler implements new rules from C++23 yet: https://gcc.godbolt.org/z/jMKW4zaaY – Fedor Jun 11 '23 at 21:31
  • 2
    The changes are in [P2564](https://github.com/cplusplus/papers/issues/1223) and it was accepted only in February of 2023 as response to a NB comment to the C++23 draft, so it is not that surprising that it hasn't been implemented yet. Actually, according to [the minutes](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4940.pdf) of the relevant meeting it was even accepted as DR against C++20, so once implemented your code should be good in C++20 mode too. – user17732522 Jun 11 '23 at 21:52

1 Answers1

8

[expr.prim.id]p3:

A potentially-evaluated id-expression that denotes an immediate function ([dcl.constexpr]) shall appear only

  • as a subexpression of an immediate invocation, or
  • in an immediate function context ([expr.const])

[expr.const]p13:

An expression or conversion is in an immediate function context if it is potentially evaluated and its innermost non-block scope is a function parameter scope of an immediate function. An expression or conversion is an immediate invocation if it is potentially-evaluated explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.

static_assert( getVec().size() == 9 ); is not in an immediate function context. Therefore, the explicit invocation getVec() is an immediate invocation, so it needs to produce a constant expression. getVec() by itself is not a constant expression since it doesn't deallocate the memory allocated by new.

For comparison, the following do compile https://godbolt.org/z/9KqhY7oP8

consteval auto getVec() {
    return std::vector<int>(9);
}

consteval auto getVecSize() {
    return getVec().size();
}

static_assert(getVecSize() == 9);
static_assert([]() consteval { return getVec().size() == 9; }());

consteval void InAnImmediateFunctionContext() {
    static_assert(getVec().size() == 9);
}

From that last example, you can see how frustrating this restriction is. This is fixed with P2564R3 being accepted as a defect report for C++20. It allows getVec() to be a subexpression of a larger constant expression without being a constant expression by itself.

This has not yet been implemented by GCC 13, but clang happily compiles your original code.

Artyer
  • 31,034
  • 3
  • 47
  • 75