c++memory-managementc++20compile-time-constant

C++20 Constexpr vector


The latest C++20 standard supports dynamic allocation memory in compile time with a limitation. The memory allocation cannot “go outside” the constant expression. That means that vector cannot be return and stored in a constexpr variable.

In the cppstories article there is the following example

#include <vector>
#include <string>
#include <algorithm>

constexpr std::vector<std::string> 
split(std::string_view strv, std::string_view delims = " ") {
    std::vector<std::string> output;
    size_t first = 0;

    while (first < strv.size()) {
        const auto second = strv.find_first_of(delims, first);

        if (first != second)
            output.emplace_back(strv.substr(first, second-first));

        if (second == std::string_view::npos)
            break;

        first = second + 1;
    }

    return output;
}

constexpr size_t numWords(std::string_view str) {
    const auto words = split(str);

    return words.size();
}

int main() {
    static_assert(numWords("hello world abc xyz") == 4);
}

This example compiles successfully. If I change

const auto words = split(str);

to

constexpr auto words = split(str);

I get following error

example.cpp (27): error C2131: expression did not evaluate to a constant (27): note: failure was caused by a read of a variable outside its lifetime (27): note: see usage of 'str' Compiler returned: 2

Based on the standard this expected since the compilers don’t support so-called “non-transient” memory allocations. But why no error is reported when const is used. In bot cases the code is executed in compile time


Solution

  • This has nothing to do with allocation.

    When you attempted to write

    constexpr auto words = split(str);
    

    Declaring a constexpr variable starts a new constant evaluation. Yes, we happen to be inside of one already (the outer static_assert call), but declaring a constexpr variable (or initializing a constant template argument or the condition of an if constexpr or ...) start a new.

    Each new constant evaluation has to be complete in of itself. Here, we don't know what str is. We didn't see its initialization. So we cannot evaluate this as a constant. That's the error:

    note: failure was caused by a read of a variable outside its lifetime
    note: see usage of 'str'


    This is the same issue as you'd see in this example:

    constexpr int plus_one(int x) {
       const int y = x + 1;
       return y;
    }
    static_assert(plus_one(4) == 5);
    

    If you change the declaration of y to be constexpr instead of const, this also wouldn't compile anymore (because: what is x?).