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
Related
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.
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.
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.
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.
I am trying to initialize a constexpr declaration with a pointer to int which is a const object. I also try to define an object with a object that is not a const type.
Code:
#include <iostream>
int main()
{
constexpr int *np = nullptr; // np is a constant to int that points to null;
int j = 0;
constexpr int i = 42; // type of i is const int
constexpr const int *p = &i; // p is a constant pointer to the const int i;
constexpr int *p1 = &j; // p1 is a constant pointer to the int j;
}
g++ log:
constexpr.cc:8:27: error: ‘& i’ is not a constant expression
constexpr.cc:9:22: error: ‘& j’ is not a constant expression
I believe it is because the objects in main have no fixed addresses, thus g++ is throwing error messages back at me; how would I correct this? Without using literal types.
Make them static to fix their addresses:
int main()
{
constexpr int *np = nullptr; // np is a constant to int that points to null;
static int j = 0;
static constexpr int i = 42; // type of i is const int
constexpr const int *p = &i; // p is a constant pointer to the const int i;
constexpr int *p1 = &j; // p1 is a constant pointer to the int j;
}
This is known as an address constant expression [5.19p3]:
An address constant expression is a prvalue core constant expression
of pointer type that evaluates to the address of an object with static
storage duration, to the address of a function, or to a null pointer
value, or a prvalue core constant expression of type std::nullptr_t.