39

I think i quite understand how to use the keyword constexpr for simple variable types, but i'm confused when it comes to pointers to values.

I would like to declare a constexpr C string literal, which will behave like

#define my_str "hello"

That means the compiler inserts the C string literal into every place where i enter this symbol, and i will be able to get its length at compile-time with sizeof.

Is it constexpr char * const my_str = "hello";

or const char * constexpr my_str = "hello";

or constexpr char my_str [] = "hello";

or something yet different?

1
  • Yes, constexpr char my_str [] = "hello" is different, better. #define silently causes problems if the same name my_str is used twice (maybe in some #include) and does not respect namespaces or the type system (e.g. #define my_str = hello itself is fine). See C++ Core Guidelines ES.31 isocpp.github.io/CppCoreGuidelines/… Commented Jan 8 at 10:57

3 Answers 3

44

Is it constexpr char * const my_str = "hello";

No, because a string literal is not convertible to a pointer to char. (It used to be prior to C++11, but even then the conversion was deprecated).

or const char * constexpr my_str = "hello";

No. constexpr cannot go there.

This would be well formed:

constexpr const char * my_str = "hello";

but it does not satify this:

So that i will be able to get its length at compile-time with sizeof, etc.


or constexpr char my_str [] = "hello";

This is well formed, and you can indeed get the length at compile time with sizeof. Note that this size is the size of the array, not the length of the string i.e. the size includes the null terminator.

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

3 Comments

I understand the value of explaining the actual type, but why not proposing using simply constexpr auto?
is constexpr auto my_str = "hello"; okay?
While Clang-Tidy might give a modernize-avoid-c-arrays warning for constexpr char s[] = "hello", there is no such warning for constexpr auto s = "hello", so from this point of view auto is even better, though not as expressive, as both result in the same type (char array).
29

In C++17, you can use std::string_view and string_view_literals

using namespace std::string_view_literals;
constexpr std::string_view my_str = "hello, world"sv;

Then,

my_str.size() is compile time constant.

2 Comments

Note that, in general, string_view::data does not guarantee a null-terminated string.
In general, yes. But in this particular case, the string_view is referring to a null-terminated string literal, so data() will be null-terminated.
1

C++11

Due to the null-termination of constexpr const char* string literals, you can simply use std::strlen, and if it ought to work compile-time, a custom function is easily definable:

constexpr std::size_t _strlen(char const* s, std::size_t count = 0) {
    return (*s == '\0') ? count : _strlen(s + 1, count + 1);
}

(C++11 constexpr functions can only contain exactly one return statement, so use recursion.)

If sizeof has to additionally work like on arrays (i.e. included the null-termination), you can define string literals using variadic templates like so:

constexpr std::size_t _strlen(char const* s, std::size_t count = 0) {
    return (*s == '\0') ? count : _strlen(s + 1, count + 1);
}

// Parameter pack helpers (similar to C++14)
template<std::size_t ...> struct _index_sequence { using type = _index_sequence; };
template<std::size_t N, std::size_t ... S> struct gen_pack_sequence: gen_pack_sequence<N - 1, N - 1, S...> {};
template<std::size_t ... S> struct gen_pack_sequence<0, S...> : _index_sequence<S...> {};
template<std::size_t N> using _make_index_sequence = typename gen_pack_sequence<N>::type;

template<char ... Chars> struct char_sequence {
    static constexpr char value[] = { Chars... };
    template<template<char...> class Template> using param_pack = Template<Chars...>;
};
template<char ... Chars> constexpr char char_sequence<Chars...>::value[];
template<char ... Chars> struct char_string: char_sequence<Chars..., '\0'> {};

// Compile-time string literals ; Concept: https://ideone.com/QvXuYf
template<typename S, typename T> struct _struct_to_string;
template<typename S, std::size_t ... Indices> struct _struct_to_string<S, _index_sequence<Indices...>> { typedef char_string<S::get()[Indices] ...> type; };
template<typename S> struct struct_to_string { typedef _make_index_sequence<_strlen(S::get())> indices; typedef typename _struct_to_string<S, indices>::type type; };
#define CONSTEXPR_STRING(name, s) \
    struct name ## __struct { constexpr static const char* get() { return s; } }; \
    typedef struct_to_string<name ## __struct>::type name

Usage with output:

CONSTEXPR_STRING(var, "Hello!");

constexpr const char* _var = var::value;
constexpr std::size_t _varlen = _strlen(_var);

int main()
{
    std::cout << _var << std::endl; // "Hello!" has length 6
    std::cout << _varlen << std::endl; // 6
    std::cout << _strlen(_var) << std::endl; // 6
    std::cout << std::strlen(_var) << std::endl; // 6
    std::cout << sizeof(var::value) << std::endl; // 7: size of { 'H', 'e', 'l', 'l', 'o', '!', '\0' }
    std::cout << sizeof(_var) << std::endl; // 8: a pointer is 64 bits = 8 bytes
}

This might seem overkill for one could simply use an array like

constexpr char _var[] = "hello";

but using ::value circumvents the problems that C-style arrays cannot be return values of functions and there are no constexpr parameters in C++. The char_string type can be extended to support all possible constexpr string features and is in particular flexible via using ::param_pack, which comes in handy for example w.r.t. compile-time string concatenation. (Here is an example of how it can be used.)

2 Comments

Why use recursion for this? It is painful in debug builds.
@AngelicosPhosphoros I found the reference on cppreference and linked it in my answer as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.