This question already has answers here:
Binding a const pointer reference to a non-const pointer
(2 answers)
Closed 11 months ago.
before writing, I'm not good at english. So maybe there are many awkward sentence.
void Func1(const int* _i) { };
void Func2(const int& _j) { };
void Func3(const int* (&_k)) { };
int main()
{
int iNum = 1;
int* pInt = new int(1);
Func1(pInt); // works;
Func2(iNum); //works
Func3(pInt); // error
}
I use Visual Studio and error message said
"Cannot convert argument 1 from 'int *' to 'const int *&'"
I know it cant convert because of '&'. _i equals pInt so it may change dereference.
But i used const. so i think it will work but const keyword doesnt work.
why const keyword doesnt work unlike other cases? ex)Func1,Func2
Func1(pInt); // works; int* could convert to const int* implicitly
Func2(iNum); //works; int could be bound to const int&
Func3(pInt); // error;
pInt is a int*, when being passed to Func3 which expects a reference to const int*, then it would be converted to const int*, which is a temporary and can't be bound to lvalue-reference to non-const like const int* & (lvalue-reference to non-const pointer to const int).
If you change the parameter type of Func3 to int* &, then no conversion is required, pInt could be bound directly. Or change to const int* const & (lvalue-reference to const pointer to const int) or const int* && (rvalue-reference) which could bind to temporary.
Related
This question already has answers here:
Why is it allowed in C++ to modify a constant object's pointer member variable's memory from outside?
(4 answers)
Closed last year.
I declared this member function to take const reference to object.. So, I am expecting that when I try to edit anything in the object there will be an error.
But in the following code I tried to edit value in a member pointer and it works !!
m_ptr is a member pointer
void Test::takeObj(const Test& obj)
{
*obj.m_ptr = 3;
}
The const property is not transitive, when dereferencing pointers. You access the pointer m_ptras read-only, but this pointer itself is not a pointer to const. You write to the object pointed to by m_ptr, not m_ptr itself.
You could write two getter functions instead of a public access to the m_ptr member variable. One getter function is const and returns a pointer to const, one is not const and returns a normal pointer.
class Test {
private:
int* m_ptr;
public:
Test(int* ptr) : m_ptr(ptr) {};
const int* ptr() const { return m_ptr; }; // this overload is called for const Test object (const this*)
int* ptr() { return m_ptr; }; // this overload is called for non-const Test object
};
int main()
{
int a;
Test x(&a); // x is non-const
const Test& x2 = x; // x2 is const
int* p = x.ptr(); // okay
*p = 3; // okay
int* p2 = x2.ptr(); // error: invalid conversion from 'const int*' to 'int*'
*p2 = 4;
}
Then inside the const takeObj you can only access the const int* version of m_ptr.
Bonus info:
On C the test object (if it only contained an int*) would be a int**. The const parameter variant would had to be a const int* const*.
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
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;
}
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.
#include <iostream>
void f(const int * & p)
{
int i =0;
i = p[0];
std::cout << i << std::endl;
}
int main()
{
int * p =new int[1];
p[0] =102;
f(p);
return 1;
}
The gcc compiler gives error for this code:
prog.cpp: In function ‘int main()’:
prog.cpp:16: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
prog.cpp:5: error: in passing argument 1 of ‘void f(const int*&)’
But if I change the "f" function into
void f(const int * const & p)
Everything is ok. Can somebody explain why const behaves this way? thanks.
Going from int* to const int* requires creating a temporary const int* pointer and binding the reference const int*& to that temporary.
The Standard forbids doing that creating of a temporary for non-const references. You therefor need to make the reference const as you did your fix.
This is because non-const references mean "I want to change the argument that the caller passes using that reference parameter". But if the caller needs to convert their argument and ends up passing a temporary, the point of the reference is for naught, and so the Standard deems it an error to try and pass the temporary.
If the first conversion (int * to const int * &) were allowed, then you could write an evil function like this:
const int really_const[] = {1,2,3};
void evil(const int * & p)
{
p = really_const;
}
int main()
{
int not_const[3];
int * p = not_const;
evil(p);
p[0] = 0; // Whoops: modifying a const object
}
The second conversion is fine, since it prevents the function from modifying the pointer in this way.