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.
Related
I know from this answer that a pointer const int** z is supposed to be read as
Variable z is [a pointer to [a pointer to a const int object]].
In my humble opinion, this would mean if z=&y then y should be a pointer to a const int object. However, the following code also compiles:
int x=0;
int const* y=&x;
const int** z=&y;
Why is an int const* object i.e. a const pointer to an int instead of a pointer to a const int acceptable to be the pointed object of z?
You are misunderstanding what the const refers to. A const always refers to the element to the left of it - unless it is the leftmost element itself, in which it refers to the element to the right.
This means that
int const * is a pointer to a const int, not a const pointer to int as you think. To get that, you would have to write int * const
int const * and const int * are two ways of writing exactly the same: a pointer to a const int.
You get it right if you read declarations from right to left. If const is the leftmost element, add a "that is" before it when reading. Ex:
const int *: pointer to int that is const.
int const *: pointer to const int.
int * const: const pointer to int.
const int * const: const pointer to int that is const.
int const * const: const pointer to const int.
Note that 1/2 are the same, and 4/5 are the same. 1 and 4 are called "west const" since const is on the west/left side, while 2 and 5 are called "east const".
Why is an int const* object i.e. a const pointer to an int
No.
int const * and const int * are the same type.
People sometimes prefer writing int const * because it reads right-to-left as "pointer to a const int", whereas const int * really reads as "pointer to an int (which is const)".
A const pointer to an int is int * const.
Try it:
int a = 42;
const int * y = &a;
int const * z = &a;
*y = 24; // compile error assigning to const
*z = 24; // compile error assigning to const
int b = 0;
y = &b;
z = &b; // re-pointing non-const pointers is fine
*z = 1; // still a compile error
int * const x = &a;
*x = 24; // fine, assigning via pointer to non-const
x = &b; // error reassigning a const pointer
have some issues with const qualifier.
I tried to do smth like this:
int a{ 3 };
int *p{ &a };
//const int **pp{ &p }; // FIXME: Compiler demands that p must have a const int *type
const int *const* pp{ &p }; // works here but it means, that I have a pointer to the const pointer to the const value
*pp = nullptr; // doesn't work cause of upper type
Results in error:
<source>:8:5: error: read-only variable is not assignable
*pp = nullptr; // doesn't work cause of upper type
~~~ ^
BUT: if i deal with new it works funny
const int **pp1{ new const int* {&p} }; // Here I have a poiner to pointer to const int, so I can change it
*pp1 = nullptr; // works
So, why does compiler demands a const int* const*? Compiler - MSVC, standart - c++17
UPD:
const int *p{ nullptr };
const int **pp{ &p }; // My intention
*pp = nullptr; // it works cause pointer non - const
But how can i get same result without const in const int *p?
There are cases where this site https://cdecl.org/ for C syntax can also help with C++. For this:
const int *const* pp
it tells me:
declare pp as pointer to const pointer to const int
For this:
const int **pp1
it tells me:
declare pp1 as pointer to pointer to const int
Let's use that....
*pp = ... // ERROR !
When you dereference pp then you get a "const pointer to const int" (because pp is "pointer to const pointer to const int"). You cannot assign to a const whatever. What counts it the top level const!
*pp1 ... // OK !?!
When you dereference pp1 you get a "pointer to const int" (because pp1 is a "pointer to pointer to const int"). A pointer to const int is not constant. What counts is the top level const! It points to a const int but the pointer itself is not const.
Conclusion: The difference between your two versions is not the use of new but the type of the pointer that you dereference (and then try to assing to the pointee).
For learning purposes, I've written the following code:
void Swap(const int *&Pointer1, const int *&Pointer2)
{
const int *Tmp = Pointer2;
Pointer2 = Pointer1;
Pointer1 = Tmp;
}
I have a few doubts about this code and how top/low level constness works in such cases, with 3 or more "levels".
Obviously my references can't be const, otherwise I wouldn't be able to swap the pointers. Let's suppose however that the code would not touch the pointers values (the adresses they contain): the right syntax would be const int *(const &Pointer) or int * const &Pointer? I have the feeling the latter would mean "reference to a const pointer to const int, but I'm not sure. And if that is the case, the const part would be ignored by the compiler like a more simple const int pass by value or would not because it is under a reference?
Trying to call this function with pointer to int fails. However, it is possible to assign an int adress to pointer to const int and indeed I get no error if I simply remove the references. This makes me think that those references "forces" every const to perfectly match. Is this true? And if so, there is some way around it?
If you want Pointer to be const too, then it would be int const * const & Pointer, let's read it from right to left; so Pointer is a reference to const pointer to const int. (Note that means both Pointer itself and the int pointed by Pointer can't be changed either. This might conflict the intent of Swap.) And both the two const parts won't be ignored when pass-by-reference. Unlike pass-by-value, reference can't be top-level const qualified, and constness qualifed on what it refers to is reserved.
You can't pass int * to the function taking const int *& (i.e. lvalue-reference to non-const pointer). int * could convert to const int* implicitly, but the converted const int* is a temporary, which can't be bound to lvalue-reference to non-const. Temporary could be bound to lvalue-reference to const (or rvalue-reference), so change the parameter type to int const * const & Pointer as stated in #1, passing int * would be fine.
template <class P1,class P2>
void Swap(P1 && Pointer1, P2 && Pointer2)
{/*...*/}
int main()
{
const int a =1, b = 2;
Swap(&a, &b); // &a and &b - r-value, Pointer1 and Pointer2 param this 'const int* &&'
const int * const a_cref_p = &a;
const int * const b_cref_p = &b;
Swap(a_cref_p,b_cref_p); // a_cref_p and b_cref_p - l-value, Pointer1 and Pointer2 param this 'const int* const &'
const int * a_ref_p = &a;
const int * b_ref_p = &b;
Swap(a_ref_p,b_ref_p); // a_ref_p and b_ref_p - l-value, Pointer1 and Pointer2 param this 'const int* &'
return 0;
}
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.