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:
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
Doesn't
constexpr
implyconst
?
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.