constexpr const vs constexpr variables? - c++

It seems obvious that constexpr implies const and thus it is common to see:
constexpr int foo = 42; // no const here
However if you write:
constexpr char *const str = "foo";
Then GCC will spawn "warning: deprecated conversion from string constant to ‘char*’" if -Wwrite-string flag is passed.
Writing:
constexpr const char *const str = "foo";
solves the issue.
So are constexpr const and constexpr really the same?

The issue is that in a variable declaration, constexpr always applies the const-ness to the object declared; const on the other hand can apply to a different type, depending on the placement.
Thus
constexpr const int i = 3;
constexpr int i = 3;
are equivalent;
constexpr char* p = nullptr;
constexpr char* const p = nullptr;
are equivalent; both make p a const pointer to char.
constexpr const char* p = nullptr;
constexpr const char* const p = nullptr;
are equivalent. constexpr makes p a const pointer. The const in const char * makes p point to const char.

The error message you're seeing has nothing to do with the constexpr keyword per se.
A string literal like "foo", as in:
somefunction("foo");
The type of this string literal is const char *. The following statement:
char *const str = "foo";
This tries to assign a const char * value to a char * value. The resulting char * value is non-mutable, constant, but by that time the error already occured: an attempt to convert a const char * to a char *.
The constexpr keyword in your example is just a distraction, and has no bearing on the error.

No. To say they are the same means that there's no time that not using const would be valid without producing functionally identical code to a const version.
I find this useful in the creation of safe singletons. I haven't explored this fully, and would expect there are other valid uses for non-const constexpr.
As an example, here is code that requires non-const constexpr:
Start with a global definition of a variable:
int global_int_;
And now we can create a constexpr function that returns a reference to it:
constexpr int& get_global()
{
return global_int_;
}
Now we can use that reference somewhere else:
int main()
{
constexpr int& i{ get_global() };
// do stuff with i
return 0;
}
We can now use i as a non-const int. If const was implied, this would not be possible.
Since non-const constexpr is valid, if you are using a constexpr that needs to be const you will need to explicitly declare it.

Related

A basic example with "constexpr" doesn't compile

I'am learning C++ so I'am currently reading the book "C++ Primer".
I was reading some examples about the "constexpr variables" and I just want to try a basic code that I just wrote but it doesn't compile I don't know why.
This is the code :
#include <iostream>
using namespace std;
int j = 8;
const int *p = &j;
int main()
{
constexpr int i = *p; // *p should be 8.
return 0;
}
The compiler says : "the value of 'p' is not usable in a constant expression"
If I replace the "constexpr" by "const" no problem at all but I think that because the value of *p should be known at compile time there shouldn't be any problems.
I don't know where I made a mistake.
(Please be tolerant my first language is French)
The reason this does not compile is because J is not const or constexpr.
Also note P is "just" const pointer.
This means *P can not be changed, but P itself is free to be changed.
Here is an example:
int f(){
int a = 5;
int b = 6;
int *p = nullptr;
p = &a; // *p -> a -> 5
p = &b; // *p -> a -> 6
return *p;
}
I want to clarify something for const vs constexpr:
const is not constexpr, but some for some primitive types there is no difference:
This compiles:
const int j = 8;
constexpr const int *p = &j;
int main(){
constexpr int i = *p;
return i;
}
Also this compiles:
constexpr int j = 8;
constexpr const int *p = &j;
int main(){
constexpr int i = *p;
return i;
}
However this does not compiles:
const int j = 8;
const int *const p = &j;
int main(){
constexpr int i = *p;
return i;
}
It seems the fundamental misunderstanding you have is the difference between an object being const vs constexpr. A variable being const means that it's logically const, i.e. the value cannot be changed once it's initialized. That does not mean that the value is known at compile time, which is what constexpr signifies.
For some type T
T t = {}; // not const, not constexpr
const T t = {}; // const, but not constexpr
constexpr T t = {}; // constexpr, also implies const
All good so far, but there's an additional wrinkle: a variable of integral or enumeration type that is const and is assigned a constant expression, is also constexpr. There are good reasons for this difference, but what that means is:
const int i = 42; // i is int, and const, so it's also constexpr !!
And of course, if you want to use an expression as a constant expression, it must have been declared as a constexpr variable or function.

What types of objects can constexpr pointers point to?

cppreference.com says:
A constexpr specifier used in an object declaration implies const.
But I tried to make a constexpr pointer hold the address of a const object of the same base-type but the compiler gave me an error:
const int a = 1;
int main(){
constexpr int *b = &a;
return 0;
}
So, what types can a constexpr pointer point to?
The issue here is not with constexpr. If you said
int *b = &a;
You'd get the same error. i.e. "invalid conversion from const int* to int*"
We can fix that by making it a pointer to a const int.
int const *b = &a;
Now we can add constexpr, and yes, constexpr does imply const
constexpr int const *b = &a;
where b is in fact const. This is exactly the same as the following
constexpr int const * const b = &a;
//^^^^^
// this const is made redundant by the constexpr.
Your example doesn't compile because 'a' is a 'const int', and requires a 'constexpr const int' pointer to point to it.

Does constexpr in pointers make a difference

What is the difference between constexpr int *np = nullptr and int const *np = nullptr?
np is a constant pointer to an int that is null, in both cases. Is there any specific use of constexpr in context of pointers.
If you try to do anything with the pointer and use the result in a constant expression, then the pointer must be marked as constexpr. The simple example is pointer arithmetic, or pointer dereferencing:
static constexpr int arr[] = {1,2,3,4,5,6};
constexpr const int *first = arr;
constexpr const int *second = first + 1; // would fail if first wasn't constexpr
constexpr int i = *second;
In the above example, second can only be constexpr if first is. Similarly *second can only be a constant expression if second is constexpr
If you try to call a constexpr member function through a pointer and use the result as a constant expression, the pointer you call it through must itself be a constant expression
struct S {
constexpr int f() const { return 1; }
};
int main() {
static constexpr S s{};
const S *sp = &s;
constexpr int i = sp->f(); // error: sp not a constant expression
}
If we instead say
constexpr const S *sp = &s;
then the above works works. Please note that the above does (incorrectly) compiled and run with gcc-4.9, but not gcc-5.1

constexpr int * vs constexpr const int * in C++ 11 [duplicate]

It seems obvious that constexpr implies const and thus it is common to see:
constexpr int foo = 42; // no const here
However if you write:
constexpr char *const str = "foo";
Then GCC will spawn "warning: deprecated conversion from string constant to ‘char*’" if -Wwrite-string flag is passed.
Writing:
constexpr const char *const str = "foo";
solves the issue.
So are constexpr const and constexpr really the same?
The issue is that in a variable declaration, constexpr always applies the const-ness to the object declared; const on the other hand can apply to a different type, depending on the placement.
Thus
constexpr const int i = 3;
constexpr int i = 3;
are equivalent;
constexpr char* p = nullptr;
constexpr char* const p = nullptr;
are equivalent; both make p a const pointer to char.
constexpr const char* p = nullptr;
constexpr const char* const p = nullptr;
are equivalent. constexpr makes p a const pointer. The const in const char * makes p point to const char.
The error message you're seeing has nothing to do with the constexpr keyword per se.
A string literal like "foo", as in:
somefunction("foo");
The type of this string literal is const char *. The following statement:
char *const str = "foo";
This tries to assign a const char * value to a char * value. The resulting char * value is non-mutable, constant, but by that time the error already occured: an attempt to convert a const char * to a char *.
The constexpr keyword in your example is just a distraction, and has no bearing on the error.
No. To say they are the same means that there's no time that not using const would be valid without producing functionally identical code to a const version.
I find this useful in the creation of safe singletons. I haven't explored this fully, and would expect there are other valid uses for non-const constexpr.
As an example, here is code that requires non-const constexpr:
Start with a global definition of a variable:
int global_int_;
And now we can create a constexpr function that returns a reference to it:
constexpr int& get_global()
{
return global_int_;
}
Now we can use that reference somewhere else:
int main()
{
constexpr int& i{ get_global() };
// do stuff with i
return 0;
}
We can now use i as a non-const int. If const was implied, this would not be possible.
Since non-const constexpr is valid, if you are using a constexpr that needs to be const you will need to explicitly declare it.

Why is const MyClass const* not valid?

I am rather new in C++ and I get confused when trying to read this kind of prototypes.
This prototype "builds" in Visual Studio 2012 (in a Native C++ Static library, not C++/CLI), although it doesn't make the pointer constant.
I noticed it throws a warning, which I didn't see at first.
C++ Visual Studio 2012
int intTest =3;
int intTest2 = 5;
const int const* pointerTest = &intTest;
pointerTest = &intTest2; //This works
const int* const pointerTest2 = &intTest;
pointerTest2 = &intTest2; //This doesn't build because the pointer is constant.
I realized of my mistake because when I was trying to build this code in Linux (Eclipse with GCC 4.6.3) it throws an error:
duplicate 'const'
I wrote this instead of const MyClass* const by mistake, but I didn't notice it because Visual Studio didn't throw an error.
Why is this syntax wrong? I didn't write it on purpose but I would like to understand.
const decorates the term to its left, unless it's the left-most, then it decorates the term to its right.
Rewrite your declaration using above rule is
int const const* pointerTest = &intTest;
You should write:
const int * const pointerTest = &intTest;
Or stick to the right-const style and won't get puzzled:
int const * const * const * const someEvilVariable = foo();
In this statement
const int const* pointerTest = &intTest;
qualifier const was simply duplicated. It is equivalent to
const int * pointerTest = &intTest;
or
int const* pointerTest = &intTest;
Here a pointer yo a constant data is being defined. The pointer itself is not a constant.
In this statement
const int* const pointerTest2 = &intTest;
you defined the pointer itself as a constant. It may be initialized only when it is defined and may not be changed.
const MyClass * and MyClass const * both declare a pointer to const data, so both consts immediately either side of MyClass are doing the exact same thing, hence the warning.
One technique for understanding what's been declared is to read the declaration from right to left e.g.
MyClass const * const pointerTest = &intTest; //a const pointer to const data of type MyClass
However, you can place the qualifier either side immediately next to the class name, like so:
const MyClass * const pointerTest = &intTest; //a const pointer to data that is const.
Other possible declarations:
const MyClass * pointerTest = &intTest; //a non-const pointer to const data.
MyClass * const pointerTest = &intTest; //a const pointer to non-const data.
Doesn't matter if the const come before or after the type (int).
Therefore
const int const* pointerTest
translates into
const int* pointerTest
since putting const twice do nothing.
What is matter is the order of const and *, so in you case:
-the first is a pointer to const int:
const int const* pointerTest = &intTest;
-and the second is a const pointer to const int: (since the const comes after the *)
const int* const pointerTest2 = &intTest;
therefore you can't change the pointer and get an error.