37

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.

6
  • 3
    Will a trivial constructor create the string object on the stack and never use dynamic allocations? There's no need for that, as dynamic allocations are permitted in constant expressions, so long as it's all deallocated before the end of the constant evaluation. Commented Jan 3, 2022 at 21:34
  • 1
    c++20 adds the ability to use constexpr with allocators cppstories.com/2021/constexpr-new-cpp20 Commented Jan 3, 2022 at 21:35
  • 1
    That variable declaration is not valid. std::string is to be usable in constant evaluation. But it's not magically a literal type. Commented Jan 3, 2022 at 21:37
  • @Artyer - Explicitly? You can't assume anything about its implementation. Commented Jan 3, 2022 at 21:42
  • @Artyer - You are neglecting the constraint on its data members. Which the standard does not impose as literal types. So again, unless it's explicitly literal, you can't proclaim it as such. Commented Jan 4, 2022 at 5:14

1 Answer 1

50

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.

Sign up to request clarification or add additional context in comments.

14 Comments

@cigien Not yet, but there haven't been any updates on it, so seems exceedingly unlikely at this point.
@Deduplicator constexpr strings can't use SSO anyway, they would always allocate.
@Deduplicator It depends on whether the SSO implementation is based on reinterpret casting. If it is, have to allocate. libstdc++'s apparently doesn't, so it doesn't need to.
@Ant they can detect it because constexpr involves actually running the code at compile time. You can do the same thing at runtime, using tools like valgrind, asan, ubsan etc. It's just not the default because these checks have a runtime cost.
@Ant: The rest of the program can have branches conditioned on runtime data (user inputs, disk file contents, network I/O). As expansive as constexpr has become, it still doesn't encompass nearly all C++ programs.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.