Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does const have to be added to constexpr for a string literal declaration?

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


Update:

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:

  • Does not apply to the characters, but rather...
  • applies to s itself, which is a pointer, and...
  • therefore is redundant with the const after the pointer.

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:

  • The characters are const via const
  • And the pointer s is const and a compile time constant via constexpr
like image 515
firebush Avatar asked May 12 '20 17:05

firebush


People also ask

Why is constexpr over const?

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.

Should I use constexpr over const?

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.

Why constexpr instead of define?

#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.

Does constexpr need to be static?

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?


2 Answers

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.

like image 96
StoryTeller - Unslander Monica Avatar answered Oct 16 '22 08:10

StoryTeller - Unslander Monica


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.

like image 3
Remy Lebeau Avatar answered Oct 16 '22 10:10

Remy Lebeau