293

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?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Vector
  • 10,879
  • 12
  • 61
  • 101
  • 7
    `std::string` is not a literal type – Piotr Skotnicki Nov 25 '14 at 09:47
  • 18
    @PiotrS - the question says that... – Vector Nov 25 '14 at 09:48
  • @Vector only literal types can be made `constexpr`. why do you need `std::string` to be constexpr? maybe there is alternative solution – Piotr Skotnicki Nov 25 '14 at 09:51
  • @PiotrS. I assume that excludes user-defined or library defined literals like `std::string_literals::operator""s` that return types with non-trivial destructors – PeterT Nov 25 '14 at 10:12
  • 1
    @PiotrS - yes, again, I understood the error message. Why? Why does anyone **need** a constexpr? We did just fine without it. It's an optimization. Also see: [When should you use constexpr capability in C++11?](http://stackoverflow.com/questions/4748083/when-should-you-use-constexpr-capability-in-c11) – Vector Nov 25 '14 at 10:18
  • 6
    @Vector did I ask you what the constexpr is for or why do you want `std::string` to be constexpr? there are several compile-time string implementations on SO. what is the point in asking if you can make a non-literal type constexpr if you understand error message and know only literal types can be made constexpr? as well there are several reasons why one may want to have a constexpr instance, so I suggest you clarify your question – Piotr Skotnicki Nov 25 '14 at 10:25
  • I agree with @PiotrS.; you already know `std::string` is impossible as `constexpr`, so obviously you *don't* need this. The next best thing is to explain what you're trying to accomplish, otherwise the default answer is to just use string literals like in my answer. – tenfour Nov 25 '14 at 10:38
  • @PiotrS. - Clarified. – Vector Nov 25 '14 at 11:06
  • 1
    @tenfour - _impossible_ - the error message says _non-trivial destructor_ - I was wondering if there was a way to circumvent or implement an alternative to that destructor in a relatively safe and painless way, so it would be _trivial_. But I suppose that would mean writing my own string class... – Vector Nov 25 '14 at 11:11
  • 4
    Yes as @PiotrS. said, there are `constexpr` string implementations out there. `std::string` is not one of them. – tenfour Nov 25 '14 at 11:12
  • 8
    @PiotrS - _there are several compile-time string implementations on SO_ - OK, thanks, understood. That's not an option for me but it answers my question: no way std::string will work. As I remarked to tenfour, I was wondering if there was a way to use std::string in way that would work. There are many tricks that I certainly am not aware of. – Vector Nov 25 '14 at 11:17
  • Thx. Pity indeed since we have some global const objects with empty strings. It is actual the destructor (with a 'free' call?) which prevents the constexpr to be kicked in. – gast128 Nov 16 '18 at 10:35
  • `const std::string constString = "constString";`, I made it by using `const` instead of `constexpr`. And I define `const` variable in a namespace. – kgbook Nov 16 '18 at 13:01
  • What exactly does it mean when some variable/object is of **literal type** and some isn't? – Milan Oct 03 '22 at 13:48

5 Answers5

285

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.

Joseph Thomson
  • 9,888
  • 1
  • 34
  • 38
  • 20
    Be aware that whenever you pass this constant to a function taking a `const std::string&` a new std::string has to be constructed. That is usually the opposite of what one had in mind when creating a constant. Therefore, I tend to say that this is not a good idea. At least you have to be careful. – Rambo Ramon Apr 04 '18 at 10:17
  • 55
    @RamboRamon `string_view` is not implicitly convertible to `string`, so there is little danger of accidentally constructing a `string` from a `string_view`. Conversely, `char const*` _is_ implicitly convertible to `string`, so using `string_view` is actually safer in this sense. – Joseph Thomson Apr 04 '18 at 13:08
  • 12
    Thanks for the clarification. I totally agree and indeed forgot that `string_view` is not implicitly convertible to `string`. IMO the problem I brought up is still valid but does not apply to `string_view` specifically. In fact, as you mentioned, it is even safer in that regard. – Rambo Ramon Apr 04 '18 at 16:25
  • 7
    It would be great if this answer said more about what `string_view` is, instead of just a link. – eric Apr 25 '18 at 13:47
  • 4
    Note that while a `std::string` has a `.c_str()` method, which returns a `char*` which is `NULL` terminated, `string_view` does *not*. Like `std::string`, it has a `.data()` method which returns a `char*` which is *not* guaranteed to be null terminated (and won't be the the `string_view` is a view into another string which has no internal `NULL`s). If you are initializing it from a compile-time constant `char` array, it *will* be `NULL` terminated, but be careful accepting `string_view` if you need to work with system calls. – Perkins Feb 24 '21 at 21:58
  • @RamboRamon Easy solution: don't use `const std::string&` as a parameter! Take `std::string_view` instead when you need a string-by-reference (there are still cases where `const std::string&` might be preferred though - e.g., when working with maps and sets which can't use `std::string_view` for keys). – PersonWithName Jun 07 '21 at 23:28
  • [Member functions of std::basic_string are constexpr: it is possible to create and use std::string objects in the evaluation of a constant expression. However, std::string objects generally cannot be constexpr, because any dynamically allocated storage must be released in the same evaluation of constant expression.](https://en.cppreference.com/w/cpp/string/basic_string) – glibg10b Aug 17 '21 at 14:35
  • @glibg10b I've edited the answer to make this clear. – Joseph Thomson Apr 19 '22 at 14:55
  • cppreference says that std::string objects generally cannot be constexpr. I'm puzzled by what generally means. With MSVC I'm allowed to write `constexpr std::string s = "foobar";` – Tohnmeister Jun 29 '23 at 12:22
  • @Tohnmesiter the standard just doesn't guarantees that `std::string` objects are *constexpr*. Guarantee is the key here, compiler vendors are allowed to play with this. I mean, is not a restriction. GCC13 and Clang 16, for example, won't compile that code, since `basic_str` doesn't have a trivial destructor – Alex Vergara Jul 18 '23 at 18:54
243

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.

JFMR
  • 23,265
  • 4
  • 52
  • 76
tenfour
  • 36,141
  • 15
  • 83
  • 142
  • 112
    Why not `constexpr auto constString = "constString";`? No need to use that ugly array syntax ;-) – stefan Nov 25 '14 at 10:19
  • 129
    In the context of this question, it's clearer. My point is about which string types you can choose from. `char[]` is more verbose / clear than `auto` when I'm trying to emphasize the data type to use. – tenfour Nov 25 '14 at 11:07
  • @stefan - Certainly it evaluates to what tenfour explained, but that's a very cool way to do it. :) – Vector Nov 25 '14 at 11:23
  • 12
    @tenfour Right, that's a good point. I guess I'm sometimes a bit too focused on using `auto` ;-) – stefan Nov 25 '14 at 12:15
  • I tried 'constexpr auto' in an 'template constexpr' but had to change -std from c++11 to c++1y when clang said 'warning: variable declaration in a constexpr function is a C++14 extension'. – kometen Jan 05 '16 at 10:41
  • @kometen: Do you need to go as far as `std=c++1y`? Why not just do `std=c++14`? (Since as clang says, it's a C++14 feature) – Anthony Hall Mar 31 '16 at 23:12
  • @squidbidness That would work too I guess. This is on xcode – kometen Apr 01 '16 at 05:58
  • Would it be possible to use a `string` literal: `constexpr auto s = "c"s;`? – Felix Dombek Mar 27 '17 at 17:00
  • 1
    @FelixDombek no, but with c++17 you could use `constexpr auto s = "c"sv;` due to the introduction of `string_view` – wich May 11 '17 at 13:55
  • 9
    Does it make sense to constexpr a char array in that context? If you use it to construct a string, it's going to be copied anyway. What's the difference between passing literal to the string's constructor and passing such a constexpr array to it? – KjMag Jul 21 '17 at 22:31
  • Runtime construction of `std::string` this way requires memory allocation and copying, so use with care in performance-sensitive code. – Bob Carpenter Nov 02 '17 at 19:40
  • @KjMag I'd just like to add that since the size of a `char[]` is known at compile time, (i.e. baked into the type for all intents and purposes), you can use some cool (and admittedly over-engineered) operations on it. I've had to write code that builds strings at compile time and verifies that character at index size is `'\0'`, before performing `&(*(char_array))` to decay back to regular char pointer, otherwise the code won't compile. The `char[]` is always created from a parameter pack of `char... c`, but not necessarily a string literal, which makes for an actual valid use case. – jfh Jan 17 '19 at 22:54
  • @stefan Because the outcome of using `auto` here is: (a) the code is less clear, (b) the code is less maintainable, (c) nothing else. AAA is evil! – Lightness Races in Orbit Jan 24 '19 at 23:26
  • 11
    @stefan usually I prefer `auto`. But there's a subtle difference: `auto` will be deduced as `const char* const`, hence `sizeof(constString)` will yield `8`. However, with the `char constString[]`-syntax, `sizeof(constString)` yields the number of characters in the string literal, which is presumably what one expects. – pasbi Jan 31 '19 at 16:36
  • 2
    `std::string_view` should be preferred over char array so as to avoid implicit conversion to `std::string`. In case you already have a requirement to pass it as a `std::string` then it could be better to define it as a `const std::string` or `static const std::string` – Mandeep Singh Jun 28 '19 at 11:11
48

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: Cannot create constexpr std::vector

Tested in Ubuntu 19.04.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
22

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;
}
neuront
  • 9,312
  • 5
  • 42
  • 71
  • 29
    This is basically what c++17 `string_view` is, except that `string_view` gives you most of the functionality that you know from `std::string` – wich May 11 '17 at 13:54
  • This is a bit of a nit, but I just wanted to point out that "can only construct from a char[] literal" isn't quite true. You can construct a local array, like `char test[6] { "hello" }; constexpr_str s(test);`. I just ran into this so just wanted to point that out. If you really want to enforce literals, one alternative is to pass a "const char*" as a template non-type parameter, although that's a bit finicky as well, depending on c++ version. – Alexander Torstling Oct 30 '22 at 12:12
12

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";
}
marcinj
  • 48,511
  • 9
  • 79
  • 100