I have the following base code:
Base Code
int main()
{
int i = 1;
const int* p = &i;
int* q = &i;
test_ptr(p);
test_ptr(q);
}
Can anyone explain why the first and third example work with the above base code, but the second one doesn't?
Example Implementation test_ptr()
Example 1 works. This works because function with pointer to const int will also accept a pointer to non-const int (but not the other way around)
void test_ptr(const int* p) // pointer to const int
{
}
Example 2 doesn't work. I don't really understand why. It is still a pointer to const int, but passed as a reference. This doesn't align with my understanding about how references work. It fails when I pass a non-const pointer to the function.
void test_ptr(const int*& p) // reference to pointer to const int
{
}
Example 3 works again and I am completely lost. So if case 2 does not work, why does it work again if I express the int* as a typedef?
typedef int* int_ptr;
void test_ptr(const int_ptr& p) // like case 2 but int* expressed as typedef
{
}
This also happens when I use pointer-to-pointer instead of reference-to-pointer.
Edit: Example 3 needs a different main function to make use of the typedef:
int main()
{
int i = 1;
const int_ptr p = &i; // use typedef here
int_ptr q = &i; // use typedef here
test_ptr(p);
test_ptr(q);
}
Example 2:
void test_ptr(const int*& p);
This works for const int* but not int* because the conversion from int* to a const int* implies a temporary and binding to a temporary has to be done using a const& for life extension to kick in.
Example 3 (when using the first main version):
typedef int* int_ptr; // or: using int_ptr = int*;
void test_ptr(const int_ptr& p);
This is the same as both of these:
void test_ptr(int_ptr const& p);
void test_ptr(int* const& p);
const is applied to the new type from right to left so it's not the int that is const, it's the pointer. The function will therefore accept int*, but not const int* since the function is allowed to change the int:s according to its signature.
The function that would accept both int* and const int* should have one of these equivalent signatures:
void test_ptr(const int* const& p);
void test_ptr(int const* const& p);
Disclaimer: I'm very unsure about the wording used in this answer
Related
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;
}
I notice that this is a valid prototype while reading through the ANSI C grammar spec from 1985 published by Jeff Lee and I compiled a function with this signature. What exactly might a function with this prototype return? What would a simple body of this function look like?
The return type is a pointer to const pointer to int. Read the declaration from right to left, and it will make things much easier. My favourite tutorial for complicated pointer declarations: http://c-faq.com/decl/spiral.anderson.html
Some (quite artificial) example:
#include <iostream>
int* const * foo(int x)
{
static int* const p = new int[x]; // const pointer to array of x ints
for(int i = 0; i < x ; ++i) // initialize it with some values
p[i] = i;
return &p; // return its address
}
int main()
{
int* const* p = foo(10); // our pointer to const pointer to int
//*p = nullptr; // illegal, cannot modify the dereferenced pointer (const)
std::cout << (*p)[8]; // display the 8-th element
}
foo is a function that takes an int parameter and returns a pointer to a const pointer to an int.
the below code doesn't compile
void aaa(const int **a) {
}
int *a[] = {new int[2]};
aaa(a);
I got "cannot convert parameter 1 from 'int [1]' to 'const int *" in VS2010 and similar error in gcc
when I change my declaration to:
int const *a[] = {new int[2]};
or
const int *a[] = {new int[2]};
it compiles, but I don't understand why it doesn't accept a non const variable declaration
The type of a is int*[]; the type you want is int const**.
int*[] converts to int**, but this will not convert implicitly to
int const**. Consider the following code to understand why:
static int const ci = 42;
void aaa( int const** out )
{
*out = &ci;
}
int
main()
{
int* pa;
aaa( &pa ); // NOT LEGAL, because...
*pa = 0; // would now change ci
std::cout << ci << std::endl;
return 0;
}
As you can see, allowing this conversion would break const without
requiring a cast.
Depending on what you are doing, you might want to use:
void aaa( int const* const* out );
The implicit conversion of int** to int const *const * is legal.
(Otherwise, you'll need a const_cast somewhere, to tell the compiler
that you know what you're doing, and that it isn't really a problem.)
The function aaa expects a pointer-to-pointer-to-constant-int.
Your variable a is a pointer-to-pointer-to-int.
It is an error to assign the latter to the former.
both int const *a[] and const int *a[] is actually the same thing, matching the signature of aaa. If you tried int * const a[], that would be a different type (pointer-to-constant-pointer-to-int) and you would trigger the type error again.
If you want your function aaa to take a constant-pointer-to-pointer-to-int, you need to write aaa(int ** const a), but having a const-ness on parameter values has actually no effect on what you can call with.
Edit: "But isn't constness added implicitly - done with an implicit cast? (Which is the actual question)"
Constness can be implicitly added to the value you are passing, e.g.
void aaa(const int a) {}
int b=5;
aaa(b);
... or one level pointer
void aaa(const int* a) {}
int *b=new int;
aaa(b);
... but cannot be added deeper. For example this is invalid:
void aaa(const int** a) {}
int* b=new int;
int** c=&b;
aaa(c);
I think James Kanze explains it much better in his answer.
I want to have a pointer to a constant, and pass its address to a function that will increment it.
int f1(const int **ptr) {
int n = **ptr; //use pointer
(*ptr)++; //increment pointer
return n;
}
void foo(const int *data) {
const int *p = data;
const int n = f1(&p); //error: invalid conversion from ‘const int**’ to ‘int**’
//error: initializing argument 1 of ‘int LevelLoader::readWord(byte**)’
}
How do I declare the pointers?
Try
int f1(const int *& ptr) {
int n = *ptr;
++ptr;
return n;
}
void foo(const int *data) {
const int *p = data;
const int n = f1(p);
}
instead.
The error message indicates that LevelLoader::readWord doesn't take a const byte**.
This:
const int *data;
is a pointer to a constant integer. That means that you are not allowed to change the data pointed to by data. You can read and copy the data, but you can't do:
(*data) = 5;
That's illegal in C++ because it was declared const int *.
If you take the address of a const int *, you get a const int **, which is a pointer to a pointer to a constant integer. So you still cannot change the integer. Any attempt to do so will cause the compiler to complain.
If you want to be able to change the integer, then foo and f1 should not take const int * values. They should take int * values.
I don't want to modify the constant. I want to modify the pointer to it (p in foo())
So, you're given a pointer. And you want to increment the pointer. So just do so:
void foo(const int *data) {
const int *p = data;
data++;
}
You have incremented the pointer. This will not affect the caller to foo, as the pointer itself is copied by value.
Check again, this code is correct. It shouldn't generate the errors you claim.
EDIT: You fixed the void problem so answer amended accordingly.