6

4th Edit: the original question title is:

Why constexpr specifier is not allowed for non-empty std::vector?

This title, as @Barry points out, is a duplicate to C++20 constexpr vector and string not working. But there is a difference in my question description: I am allocating constexpr vector in consteval function instead of normal runtime function or constexpr function.

I understand that during compile-time computing, only transient allocation is allowed, as @Barry answered in the linked question.

What I was confused about is that: for consteval function:

  1. any of its local variables will be dealloacted after it returns
  2. it only returns in compile-time, since it will only be called in compile-time
  3. so constexpr vector, as a local variable, will be deallocated in compile-time
  4. then why is it not allowed?

And my other edits below answered this confusion.

-----------------Below is the orignal question description----------

Why the following code does not compile:

consteval int foo() {
  constexpr std::vector<int> vec{1};
  return vec[0];
}

As I learned, std::vector and std::string can be used in compile time as long as both the allocation and deallocation happens in compile time. And if I delete the constexpr specifier, this code will compile.

But adding constexpr still does not violate this rule, right? Why is it not allowed?

I got this error when compiled with gcc 13 but I don't understand it:

/opt/compiler-explorer/gcc-13.1.0/include/c++/13.1.0/bits/allocator.h:195:52: error:
'std::vector<int>(std::initializer_list<int>{((const int*)(& const int [1]{1})), 1}, std::allocator<int>())' is not a constant expression because it refers to a result of 'operator new'
  195 |             return static_cast<_Tp*>(::operator new(__n));
      |                  

Edit: My question is not a duplicate to any other questions. There is only one duplicate constexpr specifier on std::vector doesn't work, but with wrong answer about why constexpr is not allowed: it states that "compile-time std::vector should be allocated and deallocated in compile-time, so it is not allowed", but the code above does not violate this rule.

2nd Edit: Thanks for answers from @user4581301 and @chris. I learned that I got a wrong understanding of a rule:

Wrong: it will be fine if memory allocated via new in compile-time is deleted also in compile-time.

Correct: in compile-time, memory allocated via new must be deleted in the same constant expression context.

And the answer to my question is that constexpr vector<int> will introduce a new constant expression context while vector<int> does not.

3rd Edit: What are the conditions that `constexpr` will start a new constant expression context?

Waker
  • 216
  • 9
  • 1
    You have to read between the lines a bit. After `constexpr std::vector vec{1};` a dynamic allocation, the one inside `vec`, would still be alive after the constant expression, and that's a no-no. – user4581301 Jul 26 '23 at 20:49
  • @user4581301 But `std::vector` is a local variable, it will be deallocated when it goes out of scope, right? And it cannot be alive in any run time context since it is `consteval` function. Also, even without `constexpr`, it is still dynamic allocation, why does it compile without `constexpr`? – Waker Jul 26 '23 at 20:51
  • 1
    `vec` is automatically allocated, but it contains inside it a pointer to a dynamically allocated array (and a couple book-keeping variables). that internal array must not exist after the constant expression, `constexpr std::vector vec{1};`, but there it is still alive on the next line at `return vec[0];`. – user4581301 Jul 26 '23 at 20:55
  • 3
    @Waker, Making the variable `constexpr` starts a new context. It's this context that the allocation escapes. Picture (conceptually) the compiler spinning up a new interpreter for evaluating a `foo` call, and then spinning up another new interpreter from the first one for the `constexpr` line. If you remove the `constexpr`, you're still in the first interpreter. – chris Jul 26 '23 at 20:56
  • 1
    Regardless, great question. One of those rare duplicates deserving of an upvote. May it live long as a useful landing page. – user4581301 Jul 26 '23 at 20:58
  • Another question deserves creating a new **Ask Question**. – Eljay Jul 26 '23 at 21:45
  • @Eljay Thanks for the advice, I will do that now. – Waker Jul 26 '23 at 21:47
  • Note the bit about how new and delete are handled that marcinj's discussing in their answer in the duplicate has moved from `[expr.const]`/5.17 and 5.18 to [`[expr.const]`/5.18 and 5.19](https://eel.is/c++draft/expr.const#5.18) – user4581301 Jul 26 '23 at 22:10
  • Yeah I'm rolling back your edits - this question *is* a duplicate of the linked question (it's asking why you can't just declare a `consetxpr vector` that does allocate something), and the answer is the answer to your question - your code does violate the rule, there's no deallocation. – Barry Jul 27 '23 at 13:09
  • @Barry Hi Barry, glad to see you response to my question. But I think you did not read my question carefully, in your linked question, `constexpr vector` allocates in `main` function, and I understand that it will definitely fail to compile since the memory allocated in compile time is not deallocated also in compile time. But in my example code above, I allocates `constexpr vector` in a `consteval` function, which will never runs in a run-time context. And I think since it is a local variable in the `consteval` function, it will get deallocated when the function return. – Waker Jul 28 '23 at 00:22
  • @Barry I admit that the title of my question should be more specific, it indeed looks like a duplicate, I should ask "Why `constexpr` specifier is not allowed for non-empty `std::vector` in `consteval` function?" – Waker Jul 28 '23 at 00:55
  • @Barry And I also understand that I can just use `vector` here since even if I can declare `constexpr vector` I cannot modify it since it is `const`, while with just `vector` I can use and modify it in compile time. I am asking this question just with the purpose of understanding a language trick here. And the edits you rolled back answered my confusion. – Waker Jul 28 '23 at 01:04

0 Answers0