Is it possible to use std::string in a constexpr?

C++C++11ConstexprStdstring

C++ Problem Overview


Using C++11, Ubuntu 14.04, GCC default toolchain.

This code fails:

constexpr std::string constString = "constString";

> error: the type ‘const string {aka const std::basic_string}’ of > constexpr variable ‘constString’ is not literal... because... > ‘std::basic_string’ has a non-trivial destructor

Is it possible to use std::string in aconstexpr? (apparently not...) If so, how? Is there an alternative way to use a character string in a constexpr?

C++ Solutions


Solution 1 - C++

As of C++20, yes, but only if the std::string is destroyed by the end of constant evaluation. So while your example will still not compile, something like this will:

constexpr std::size_t n = std::string("hello, world").size();

However, as of C++17, you can use string_view:

constexpr std::string_view sv = "hello, world";

A string_view is a string-like object that acts as an immutable, non-owning reference to any sequence of char objects.

Solution 2 - C++

No, and your compiler already gave you a comprehensive explanation.

But you could do this:

constexpr char constString[] = "constString";

At runtime, this can be used to construct a std::string when needed.

Solution 3 - C++

C++20 will add constexpr strings and vectors

The following proposal has been accepted apparently: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0980r0.pdf and it adds constructors such as:

// 20.3.2.2, construct/copy/destroy
constexpr
basic_string() noexcept(noexcept(Allocator())) : basic_string(Allocator()) { }
constexpr
explicit basic_string(const Allocator& a) noexcept;
constexpr
basic_string(const basic_string& str);
constexpr
basic_string(basic_string&& str) noexcept;

in addition to constexpr versions of all / most methods.

There is no support as of GCC 9.1.0, the following fails to compile:

#include <string>

int main() {
    constexpr std::string s("abc");
}

with:

g++-9 -std=c++2a main.cpp

with error:

error: the type ‘const string’ {aka ‘const std::__cxx11::basic_string<char>’} of ‘constexpr’ variable ‘s’ is not literal

std::vector discussed at: https://stackoverflow.com/questions/33241909/cannot-create-constexpr-stdvector

Tested in Ubuntu 19.04.

Solution 4 - C++

Since the problem is the non-trivial destructor so if the destructor is removed from the std::string, it's possible to define a constexpr instance of that type. Like this

struct constexpr_str {
    char const* str;
    std::size_t size;

    // can only construct from a char[] literal
    template <std::size_t N>
    constexpr constexpr_str(char const (&s)[N])
        : str(s)
        , size(N - 1) // not count the trailing nul
    {}
};

int main()
{
    constexpr constexpr_str s("constString");

    // its .size is a constexpr
    std::array<int, s.size> a;
    return 0;
}

Solution 5 - C++

C++20 is a step toward making it possible to use std::string at compile time, but P0980 will not allow you to write code like in your question:

constexpr std::string constString = "constString";

the reason is that constexpr std::string is allowed only to be used in constexpr function (constant expression evaluation context). Memory allocated by constexpr std::string must be freed before such function returns - this is the so called transient allocation, and this memory cannot 'leak' outside to runtime to constexpr objects (stored in data segments) accessible at runtime . For example compilation of above line of code in current VS2022 preview (cl version : 19.30.30704) results in following error:

1> : error C2131: expression did not evaluate to a constant
1> : message : (sub-)object points to memory which was heap allocated during constant evaluation

this is because it tries to make a non-transient allocation which is not allowed - this would mean allocation into a data segment of the compiled binary.

In p0784r1, in "Non-transient allocation" paragraph, you can find that there is a plan to allow conversion of transient into static memory (emphasis mine):

> What about storage that hasn't been deallocated by the time evaluation > completes? We could just disallow that, but there are really > compelling use cases where this might be desirable. E.g., this could > be the basis for a more flexible kind of "string literal" class. We > therefore propose that if a non-transient constexpr allocation is > valid (to be described next), the allocated objects are promoted to > static storage duration.

There is a way to export transient std::string data outside to make it usable at runtime. You must copy it to std::array, the problem is to compute the final size of std::array, you can either preset some large size or compute std::string twice - once to get size and then to get atual data. Following code successfully compiles and runs on current VS2022 preview 5. It basicly joins three words with a delimiter between words:

constexpr auto join_length(const std::vector<std::string>& vec, char delimiter) {
  std::size_t length = std::accumulate(vec.begin(), vec.end(), 0,
    [](std::size_t sum, const std::string& s) {
      return sum + s.size();
    });
  return length + vec.size();
}

template<size_t N>
constexpr std::array<char, N+1> join_to_array(const std::vector<std::string>& vec, char delimiter) {
  std::string result = std::accumulate(std::next(vec.begin()), vec.end(),
    vec[0],
    [&delimiter](const std::string& a, const std::string& b) {
      return a + delimiter + b;
    });
  std::array<char, N+1> arr = {};
  int i = 0;
  for (auto c : result) {
    arr[i++] = c;
  }
  return arr;
}
constexpr std::vector<std::string> getWords() {
  return { "one", "two", "three" };
}

int main()
{
  constexpr auto arr2 = join_to_array<join_length(getWords(), ';')>(getWords(), ';');
  static_assert(std::string(&arr2[0]) == "one;two;three");
  std::cout << &arr2[0] << "\n";
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVectorView Question on Stackoverflow
Solution 1 - C++Joseph ThomsonView Answer on Stackoverflow
Solution 2 - C++tenfourView Answer on Stackoverflow
Solution 3 - C++Ciro Santilli Путлер Капут 六四事View Answer on Stackoverflow
Solution 4 - C++neurontView Answer on Stackoverflow
Solution 5 - C++marcinjView Answer on Stackoverflow