Is it possible to use std::string in a constexpr?
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.
constexpr std::string in C++20, how does it work?
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.
C++ constexpr std::array of string literals
As user17732522 already noted, the type deduction for your original code produces a const std::array<const char*, 3>
. This works, but it's not a C++ std::string
, so every use needs to scan for the NUL
terminator, and they can't contain embedded NUL
s. I just wanted to emphasize the suggestion from my comment to use std::string_view
.
Since std::string
inherently relies on run-time memory allocation, you can't use it unless the entirety of the associated code is also constexpr
(so no actual string
s exist at all at run-time, the compiler computes the final result at compile-time), and that's unlikely to help you here if the goal is to avoid unnecessary runtime work for something that is partially known at compile time (especially if the array
gets recreated on each function call; it's not global or static
, so it's done many times, not just initialized once before use).
That said, if you can rely on C++17, you can split the difference with std::string_view
. It's got a very concise literal form (add sv
as a prefix to any string literal), and it's fully constexpr
, so by doing:
// Top of file
#include <string_view>
// Use one of your choice:
using namespace std::literals; // Enables all literals
using namespace std::string_view_literals; // Enables sv suffix only
using namespace std::literals::string_view_literals; // Enables sv suffix only
// Point of use
constexpr std::array myStrings = { "one"sv, "two"sv, "three"sv };
you get something that involves no runtime work, has most of the benefits of std::string
(knows its own length, can contain embedded NUL
s, accepted by most string-oriented APIs), and therefore operates more efficiently than a C-style string for the three common ways a function accepts string data:
- For modern APIs that need to read a string-like thing, they accept
std::string_view
by value and the overhead is just copying the pointer and length to the function - For older APIs that accept
const std::string&
, it constructs a temporarystd::string
when you call it, but it can use the constructor that extracts the length from thestd::string_view
so it doesn't need to prewalk a C-style string withstrlen
to figure out how much to allocate. - For any API that needs a
std::string
(because it will modify/store its own copy), they're receivingstring
by value, and you get the same benefit as in #2 (it must be built, but it's built more efficiently).
The only case where you do worse by using std::string_view
s than using std::string
is case #2 (where if the std::array
contained std::string
s, no copies would occur), and you only lose there if you make several such calls; in that scenario, you'd just bite the bullet and use const std::array myStrings = { "one"s, "two"s, "three"s };
, paying the minor runtime cost to build real string
s in exchange for avoiding copies when passing to old-style APIs taking const std::string&
.
store a string in a constexpr struct
You might do:
template<typename Char, Char... Cs>
struct CharSeq
{
static constexpr const Char s[] = {Cs..., 0}; // The unique address
};
// That template uses the extension
template<typename Char, Char... Cs>
constexpr CharSeq<Char, Cs...> operator"" _cs() {
return {};
}
See my answer from String-interning at compiletime for profiling to have MAKE_STRING
macro if you cannot used the extension (Really more verbose, and hard coded limit for accepted string length).
Then
struct A
{
template <char ... Cs>
constexpr A(CharSeq<char, Cs...>) : m_name(CharSeq<char, Cs...>::s) {}
constexpr auto name(){ return m_name; }
std::string_view m_name;
};
With only valid usages similar to:
A a = {"Hello"_cs};
constexpr A b = {"World"_cs};
Can I create constexpr strings with functions?
Can I create constexpr strings with functions?
You can't return std::string
, cause it allocates memory.
Can I make these as a constexpr ?
Sure, something along:
#include <array>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
template<size_t N> constexpr
auto create_header(const char (&comment_symbol)[N]) {
const char one[] = " Begin of File.\n";
const char two[] = " -\n";
std::array<char,
N + (sizeof(one) - 1) +
N + (sizeof(two) - 1) + 1
> ret{};
auto it = ret.begin();
for (const char *i = comment_symbol; *i; ++i) *it++ = *i;
for (const char *i = one; *i; ++i) *it++ = *i;
for (const char *i = comment_symbol; *i; ++i) *it++ = *i;
for (const char *i = two; *i; ++i) *it++ = *i;
return ret;
}
std::string c_header() {
constexpr auto a = create_header("//");
return std::string{a.begin(), a.end()};
}
int main() {
std::cout << c_header() << '\n';
}
How to create a constexpr std::vector std::string or something similar?
std::vector
/std::string
doesn't have constexpr
constructor before C++20...
and even in C++20, the constexpr
allocation should not escape from constexpr evaluation, so cannot be used in runtime (for printing).
I don't see a standard constexpr way to transform an integer to a char sequence representation.std::to_string
, std::to_chars
, std::format
are not constexpr
.
So instead of homogeneous container, you might use std::tuple
, something like (C++17):
template <std::size_t I>
constexpr auto fizzbuzz_elem()
{
if constexpr (I % 5 == 0 && I % 3 == 0) {
return "FizzBuzz";
} else if constexpr (I % 5 == 0) {
return "Buzz";
} else if constexpr (I % 3 == 0){
return "Fizz";
} else {
return I;
}
}
template <std::size_t...Is>
constexpr auto fizzbuzz_impl(std::index_sequence<Is...>){
return std::make_tuple(fizzbuzz_elem<1 + Is>()...);
}
template <std::size_t N>
constexpr auto fizzbuzz(){
return fizzbuzz_impl(std::make_index_sequence<N>());
}
int main() {
constexpr auto res = fizzbuzz<42>();
std::apply([](auto... e){ ((std::cout << e << std::endl), ...); }, res);
}
Demo
c++ constexpr concatenate char*
From constexpr you cannot return char* which is constructed there... You must return some compile time known(also its size) constant thingy.
A possible solution could be something like:
#include <cstring>
// Buffer to hold the result
struct NameBuffer
{
// Hardcoded 128 bytes!!!!! Carefully choose the size!
char data[128];
};
// Copy src to dest, and return the number of copied characters
// You have to implement it since std::strcpy is not constexpr, no big deal.
constexpr int constexpr_strcpy(char* dest, const char* src);
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr NameBuffer name()
{
// We will return this
NameBuffer buf{};
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
// Copy them one by one after each other
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
// Null terminate the buffer, or you can store the size there or whatever you want
buf.data[n] = '\0';
return buf;
}
Demo
And since the returned char* is only depends on the template parameter in your case, you can create templated variables, and create a char* to them, and it can act like any other char*...
EDIT:
I have just realized that your pseudo code will never work!! Inside name<T>()
you are trying to call name<T>()
.
You must redesign this!!! But! With some hack you can determine the size at compile time somehow for example like this:
#include <cstring>
#include <iostream>
template<std::size_t S>
struct NameBuffer
{
char data[S];
};
// Copy src to dest, and return the number of copied characters
constexpr int constexpr_strcpy(char* dest, const char* src)
{
int n = 0;
while((*(dest++) = *(src++))){ n++; }
return n;
}
// Returns the len of str without the null term
constexpr int constexpr_strlen(const char* str)
{
int n = 0;
while(*str) { str++; n++; }
return n;
}
// This template parameter does nothing now...
// I left it there so you can see how to create the template variable stuff...
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr auto createName()
{
constexpr const char* collectionName = "std::vector";
constexpr const char* containedTypeName = "dummy";
constexpr std::size_t buff_size = constexpr_strlen(collectionName) +
constexpr_strlen(containedTypeName) +
2; // +1 for <, +1 for >
/// +1 for the nullterm
NameBuffer<buff_size + 1> buf{};
/// I'm lazy to rewrite, but now we already calculated the lengths...
int n = constexpr_strcpy(buf.data, collectionName);
n += constexpr_strcpy(buf.data + n, "<");
n += constexpr_strcpy(buf.data + n, containedTypeName);
n += constexpr_strcpy(buf.data + n, ">");
buf.data[n] = '\0';
return buf;
}
// Create the buffer for T
template<typename T>
static constexpr auto name_buff_ = createName<T>();
// point to the buffer of type T. It can be a function too as you wish
template<typename T>
static constexpr const char* name = name_buff_<T>.data;
int main()
{
// int is redundant now, but this is how you could use this
std::cout << name<int> << '\n';
return 0;
}
Demo
Related Topics
Is Storing an Invalid Pointer Automatically Undefined Behavior
How Can a C++ Header File Include Implementation
Std::Set with User Defined Type, How to Ensure No Duplicates
Sharing a Global/Static Variable Between a Process and Dll
Measure Execution Time in C++ Openmp Code
No Match for 'Operator<<' in 'Std::Operator
Why Should I Use the "Using" Keyword to Access My Base Class Method
Why Would Someone Use #Define to Define Constants
Stack Overflow Caused by Recursive Function
Is Accessing Data in the Heap Faster Than from the Stack
How Is a Vector's Data Aligned
Why Is the New Random Library Better Than Std::Rand()
Practical Use of Zero-Length Bitfields