I would like to know why I get the compile error for the first "const method", while the second one is OK.
typedef struct xyz{
uint16_t xyz1[16];
}XYZ;
class A {
public :
A() :m_a1(4) { m_a2 = new XYZ[4]; }
void getSomething() const
{
uint16_t* p = m_a1[0].xyz1; //error: invalid conversion from
//'const uint16_t* {aka const short unsigned int*}'
//to 'uint16_t* {aka short unsigned int*}'
}
voi getSomething2() const
{
uint16_t* p = m_a2[0].xyz1; //compile OK
}
private:
vector<XYZ> m_a1;
XYZ* m_a2;
};
In a const member function, all non-static data members are considered as const too, then m_a1 becomes const vector<XYZ>. There's a const overload for std::vector::operator[], so perform operator[] on a const std::vector<XYZ> you'll get a const XYZ; whose member xyz1 becomes const too; i.e. const uint16_t [16], which will decay to const uint16_t* and can't be converted to uint16_t* implicitly.
On the other hand, for the data member m_a2 with type XYZ* in the const member function, will become XYZ* const (note it's not const XYZ*). i.e. the pointer itself is const, but not the pointee. Then with operator[], it'll still return a XYZ, not a const XYZ. That's why it works well for the following statement.
The difference is that in the first methodn, the vector object is const, so only const methods of it are used, so the [] operator returns a const reference, and the member xyz1 is const too.
In the second method, the pointer m_a2 is const, but not the memory location it points to. That's why you don't need the const in the second case.
If the member function is const all member non mutable variables accessed within that method are also treated as const, so the compiler will not allow you to make a non-pointer-to-const point to const member data. Change your code to this
void getSomething() const
{
const uint16_t* p = m_a1[0].xyz1;
// or simply
// auto* p = m_a1[0].xyz1
}
In the second one, the pointer itself is const, but not the data it is pointing to. So instead of m_a2 being a const XYZ* (which is a pointer to const) like you think, its XYZ* const which is a const pointer.
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*.
let's have an example program:
struct Example
{
int* pointer;
Example(int* p) : pointer(p) {}
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer = &var; // error
*object.pointer = var;
}
I do know why the error appears - when creating a const Example object, pointer is actually const pointer, not a pointer to const. Therefore, when assigning &var to object.pointer it is not correct but assigning var to the *object.pointer works perfectly fine.
So is there any way to make this line:
*object.pointer = var;
not compile? I want the value that pointer points to to also be const. In other words, when creating a const object:
const Example object(&var);
I want constness to be applied also on the value itself, not only on the pointer:
const int* const pointer; // something like that
Either declare the data member like
const int *pointer;
or make it a protected or private data member. In the last case you can declare a non-constant member function that allows to change the value pointed to by the pointer. For a constant object of the class the function will not be called. For example
struct Example
{
private:
int* pointer;
public:
Example(int* p) : pointer(p) {}
int & get_value() { return *pointer; }
const int & get_value() const { return *pointer; }
};
const Example object(&var);
This creates a const object. Once a const object is created it cannot be changed. This is what a constant object means, by definition.
object.pointer = &var;
This attempts to change this object's member. This object cannot be changed. It is a constant object now, hence the error.
I want constness to be applied also on the value itself, not only on
the pointer:
const int* const pointer; // something like that
"Something like that" declares a pointer that's both a constant value itself, and it points to a constant value.
If you want an object with a pointer to const int, that would be:
const int *pointer;
And making the object itself constant has absolutely nothing to do with it, whatsoever. It only prevents modification of the object itself. Once you make something const it cannot be changed for any reason.
You can do something like that by
Making pointer an member function that return the pointer
Having non-const version of the function return a reference to the pointer to allow assignment to that
Having const version of the function return a copy of the pointer to prevent assignment
struct Example
{
private:
int* pointer_internal;
public:
Example(int* p) : pointer_internal(p) {}
int*& pointer() { return pointer_internal; }
int* pointer() const { return pointer_internal; }
};
int main()
{
int var = 30;
const Example object(&var);
object.pointer() = &var; // error: lvalue required as left operand of assignment
*object.pointer() = var;
}
why error happend, I think const auto data_0 should be the same with const Data* data_1,
what's the difference bwtween data_0 and data_1?
class Data {
public:
Data(int val_) : val(val_) {
}
~Data() {
}
void SetVal(int val_) {
val = val_;
}
private:
int val;
};
Data* GetData(int val) {
return new Data(val);
}
int main () {
const auto data_0 = GetData(0);
const Data* data_1 = GetData(0);
data_0->SetVal(1); // OK
data_1->SetVal(1); // error: passing ‘const Data’ as ‘this’ argument discards qualifiers
return 0;
}
const applies to the thing on its left, unless there is nothing then it applies to the thing on its right.
In const auto data_0 = GetData(0);, the const applies to whatever type auto deduces to, which in this case is Data*. So, the final declaration is:
Data * const data_0 = GetData(0);
The const applies to the pointer itself, not the thing it is pointing at. Thus, data_0 is a const pointer to a non-const Data object. SetVal() is a non-const method, so it can be called on the object.
In const Data* data_1 = GetData(0);, the const applies to Data. So, the final declaration is:
Data const * data_1 = GetData(0);
The const applies to the thing being pointed at, not to the pointer itself. Thus, data_1 is a non-const pointer to a const Data object. SetVal() is not a const method, so it cannot be called on the object.
what's difference between const auto and const type*
const auto will be deduced from the initialiser and will be a const qualified type. In case of data_0, the type of the initialiser is a pointer to non-const Data and therefore the deduced type becomes a const qualified pointer to non-const Data i.e. Data* const.
const type* is a non-const qualified pointer to a const qualified object of type type. The difference is that one is non-const pointer to const and the other is const pointer to non-const.
why error happend
Because you call a non-const qualified member function through a pointer to const.
I think const auto data_0 should be the same with const Data* data_1
It isn't, and it shouldn't be.
Considering the following code :
#include <iostream>
#include <vector>
template<typename Type> class MyClass
{
public:
MyClass(Type* ptr) : _ptr{ptr}, _val{*ptr} {;}
inline Type*& getptr() {return _ptr;}
inline Type*& getptrc() const {return _ptr;}
inline Type& getval() {return _val;}
inline Type& getvalc() const {return _val;}
protected:
Type* _ptr;
Type _val;
};
int main()
{
std::vector<double> v = {0, 1, 2};
MyClass<const double> x(&v[0]);
x.getval();
x.getvalc(); // <- OK
x.getptr();
x.getptrc(); // <- ERROR : "invalid initialization of reference of type 'const double*&' from expression of type 'const double* const'"
return 0;
}
GCC produce an error for the getptrc function invalid initialization of reference of type 'const double*&' from expression of type 'const double* const'. But the function getvalc compiles well. I do not understand the difference between getvalc and getptrc that is at the origin of the error.
What is the cause of the error and why I can't put a const for a function that returns a reference to a pointer ?
const double*& is a reference to a pointer to a const double.
const double* const is a const pointer to a const double.
This means that you have to return a constant pointer.
inline Type* const & getptrc() const {return _ptr;}
const on methods means that you will not modify the data member. To fullfill that contract, you have to return a constant pointer because otherwise, you could modify the data member _ptr. However, in your other case with getvalc, you already have fullfilled that contract by returning a const double.
In MyClass<const double>, the name Type refers to const double. So the member function getvalc(), which returns a Type&, is fine: it returns a const double&, and _val cannot be modified through that reference.
There's another layer of indirection for Type*&; although Type is const double, the pointer that points to it is modifiable; that's why the compiler is complaining: _ptr is const inside the const member function, but the function attempts to return it as a modifiable pointer.
if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue expression whose value is the address
of the object for which the function is called. The type of this in a member function of a class X is X*. If the member
function is declared const, the type of this is const X*, if the member function is declared volatile, the type of
this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile
X*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};