Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does constexpr std::string in C++20 work? [duplicate]

Apparently, the constexpr std::string has not been added to libstdc++ of GCC yet (as of GCC v11.2).

This code:

#include <iostream>
#include <string>

int main()
{
    constexpr std::string str { "Where is the constexpr std::string support?"};

    std::cout << str << '\n';
}

does not compile:

time_measure.cpp:37:31: error: the type 'const string' {aka 'const std::__cxx11::basic_string<char>'} of 'constexpr' variable 'str' is not literal
   37 |         constexpr std::string str { "Where is the constexpr std::string support?"};
      |                               ^~~
In file included from c:\mingw64\include\c++\11.2.0\string:55,
                 from c:\mingw64\include\c++\11.2.0\bits\locale_classes.h:40,
                 from c:\mingw64\include\c++\11.2.0\bits\ios_base.h:41,
                 from c:\mingw64\include\c++\11.2.0\ios:42,
                 from c:\mingw64\include\c++\11.2.0\ostream:38,
                 from c:\mingw64\include\c++\11.2.0\iostream:39,
                 from time_measure.cpp:2:
c:\mingw64\include\c++\11.2.0\bits\basic_string.h:85:11: note: 'std::__cxx11::basic_string<char>' is not literal because:
   85 |     class basic_string
      |           ^~~~~~~~~~~~
c:\mingw64\include\c++\11.2.0\bits\basic_string.h:85:11: note:   'std::__cxx11::basic_string<char>' does not have 'constexpr' destructor

How will such strings work under the hood when a string contains more than 16 chars (because GCC's SSO buffer size is 16)? What would be a brief explanation? Will a trivial constructor create the string object on the stack and never use dynamic allocations?

This code:

    std::cout << "is_trivially_constructible: "
              << std::boolalpha << std::is_trivially_constructible<const std::string>::value << '\n';

prints this:

is_trivially_constructible: false

Now by using constexpr here (obviously does not compile with GCC v11.2):

    std::cout << "is_trivially_constructible: "
              << std::boolalpha << std::is_trivially_constructible<constexpr std::string>::value << '\n';

will the result be true like below?

is_trivially_constructible: true

My goal

My goal was to do something like:

    constexpr std::size_t a { 4 };
    constexpr std::size_t b { 5 };
    constexpr std::string msg { std::format( "{0} + {1} == {2}", a, b, a + b ) };

    std::cout << msg << '\n';

Neither std::format nor constexpr std::string compile on GCC v11.2.

like image 638
digito_evo Avatar asked Aug 31 '25 17:08

digito_evo


1 Answers

C++20 supports allocation during constexpr time, as long as the allocation is completely deallocated by the time constant evaluation ends. So, for instance, this very silly example is valid in C++20:

constexpr int f() {
    int* p = new int(42);
    int v = *p;
    delete p;
    return v;
}

static_assert(f() == 42);

However, if you forget to delete p; there, then f() is no longer a constant expression. Can't leak memory. gcc, for instance, rejects with:

<source>:2:24: error: '(f() == 42)' is not a constant expression because allocated storage has not been deallocated
    2 |     int* p = new int(42);
      |                        ^

Getting back to your question, std::string will work in constexpr for long strings just fine -- by allocating memory for it as you might expect. However, the C++20 constexpr rules are still limited by this rule that all allocations must be cleaned up by the end of evaluation. Alternatively put, all allocations must be transient - C++ does not yet support non-transient constexpr allocation.

As a result, your original program

int main( )
{
    constexpr std::string str { "Where is the constexpr std::string support?"};
}

is invalid, even once gcc supports constexpr string (as it does on trunk right now), because str needs to be destroyed. But this would be fine:

constexpr int f() {
    std::string s = "Where is the constexpr std::string support?";
    return s.size();
}

static_assert(f() > 16);

whereas it would not have compiled in C++17.


There still won't be support for non-transient constexpr allocation in C++23. It's a surprisingly tricky problem. But, hopefully soon.

like image 87
Barry Avatar answered Sep 02 '25 16:09

Barry