This declaration:
char constexpr *const s = "hello";
Fails with this error:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
test.cc:8:31: error: ISO C++11 does not allow conversion from string literal to 'char *const' [-Werror,-Wwritable-strings]
char constexpr *const s = "hello";
But if I add const to constexpr, the compiler is happy:
char const constexpr *const s = "hello";
Compilation:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
hello
This seems unintuitive to me. Why does const need to decorate constexpr? Doesn't constexpr imply const? If it's a compiler constant, how is it not a constant in some other sense? Is it possible for something to be constexpr but change in some other way such that is not constant?
Here's a minimal godbolt:
https://godbolt.org/z/sSQMVa
StoryTeller's answer has the key to understanding this. I've accepted his answer, but I'll expand on it here in case that's helpful for someone else trying to understand this. When interacting with const, I'm used to thinking of const as applying to the item on the left of it. Thus:
char a[] = "hello";
char * const s = a;
s[0] = 'H'; // OK
s = "there"; // Compiler error.
Here, char * const s
means that the pointer, s, is const while the characters it dereferences to are modifiable. On the other hand:
char const * s = "hello";
a[0] = 'H'; // Compiler error
s = "there"; // OK
In this case, char const * s
means that the characters that s points to are const, not the pointer.
OK, most people who have worked with const and pointers understand all that. Where I got thrown off is that I assumed that constexpr would work this way as well. That is, given this:
char constexpr * const s = "hello";
I thought that would mean that the pointer is const (it is) and that the characters themselves would be const and constexpr. But the syntax doesn't work that way. Rather, the constexpr in this case:
s
itself, which is a pointer, and...Thus, in this case, there is no const being declared on the characters. Indeed, if I remove the constexpr entirely I get the exact same error:
char * const s = "hello"; // Produces same error as char constexpr * const s = "hello";
This, however, works:
constexpr char const * s = "hello";
The above has what we want and it means:
const
s
is const and a compile time constant via constexpr
Like const , it can be applied to variables: A compiler error is raised when any code attempts to modify the value. Unlike const , constexpr can also be applied to functions and class constructors. constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time.
const can only be used with non-static member function whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types. You read about more limitations here.
#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.
A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?
Doesn't constexpr imply const?
It does, on the object being declared, in your case s
. The result of applying constexpr
is the object
char *const s;
It's still declared to point at a non-const object. Only the address has to be a constant expression. Which means it must be to an object with static storage duration.
Is it possible for something to be constexpr but change in some other way such that is not constant?
No. But then again, it's not the object being declared constexpr
that is allowed to change here. For instance
static char foo[] = "abc"; // Not a constant array
constexpr char * s = foo; // But the address is still a valid initializer.
Is a valid pair of declarations.
const
applies to the thing on its left, or if nothing is there then to its right.
In char *const s = "hello";
, the const
is applied to the *
, not to the char
, so s
is a const pointer to non-const char
data. However, a string literal is const char data (in this case, "hello"
is a const char[6]
). You can't have a pointer to non-const data that is actually pointing at const data, that would allow the const data to be modifiable, which is undefined behavior if something actually tried to modify the data. That is what the compiler error is complaining about.
So, you need a pointer to const char data instead:
char const *const s = "hello";
Or:
const char *const s = "hello";
The constexpr
just makes the s
variable available for evaluation at compile-time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With