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.
Related
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.
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.
My understanding of const references to a class was that we cannot modify the state of the class i.e. cannot perform any action that modifies any of it's member variables. But consider the following code.
#include <iostream>
#include <vector>
struct Vec {
Vec(std::vector<int>& v) : vec(v) {}
Vec(const Vec& v) : vec(v.vec) {
v.vec.resize(100);
}
std::vector<int>& vec;
};
int main() {
std::vector<int> x;
Vec v1(x);
Vec v2(v1);
v1.vec.resize(10, 0);
v2.vec[5] = 8;
std::cout << v1.vec[5] << std::endl;
return 0;
}
I compiled this code using g++:4.8.3 with -Wall flag and it compiled. I have two questions.
In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector<int>&. Then how does it get copied to type std::vector<int> ?
The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?
The constness of a class applies to its member variables, but not to the referents of the member variables that are references. This is similar to pointer members, where the pointer would be const, but not what it points to.
In Vec copy construtor, we pass a const reference to a Vec v. By extension of constness of v, v.vec should also have been a const std::vector&. Then how does it get copied to type std::vector ?
The only logical way the above can happen is if constness of a class doesn't apply to it's member variable. So my question is what implications does a constness of a class has on it's member variables ?
That's not really how const extends. You stick the const on the back of the type, not the front. Consider the following code...
struct foo {
foo() {}
int * i;
};
int main (void)
{
foo my_foo;
int * &a = my_foo.i;
const int * &b = my_foo.i;
int * const &c = my_foo.i;
const foo const_foo;
int * &d = const_foo.i;
const int * &e = const_foo.i;
int * const &f = const_foo.i;
return 0;
}
foo.cpp: In function ‘int main()’:
foo.cpp:12: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int*’
foo.cpp:16: error: invalid initialization of reference of type ‘int*&’ from expression of type ‘int* const’
foo.cpp:17: error: invalid initialization of reference of type ‘const int*&’ from expression of type ‘int* const’
This shows that const_foo.i has type int * const, which is different from const int *. The type int * const makes no promises about its pointed at data not changing, just the pointer itself can't change.
In your example, v2.vec would have type std::vector<int> & const. But that type is meaningless (and illegal) because you can't change the alias of a reference anyhow. std::vector<int> is already const for this purpose.
It is possible to have const-ness inherit, but you have to explicitly code that rule. The following code will happily refuse to compile due to enforcing const restrictions by restricting callers to use a getter that makes the contract you are looking for...
#include <iostream>
#include <vector>
struct Vec {
Vec(std::vector<int>& v) : _vec(v) {}
Vec(const Vec& v) : _vec(v.vec()) {
v.vec().resize(100);
}
// How to make const-ness inherit...
std::vector<int> & vec() { return _vec; }
std::vector<int> const & vec() const { return _vec; }
private:
std::vector<int>& _vec;
};
int main() {
std::vector<int> x;
Vec v1(x);
Vec v2(v1);
v1.vec().resize(10, 0);
v2.vec()[5] = 8;
std::cout << v1.vec()[5] << std::endl;
return 0;
}
Once you start doing that you get into strange territory however, as it allows me to call std::vector<int> const & vec() const, save a 'data-const' reference to _vec, and then have other code change the data in vec, violating the const contract to the earlier code. There are a number of landmines out there, which may be why the language doesn't have this sort of const inheritance built-in.
I wrote an abstract class
class A
{
enum SegmentationType { SOURCE_SEGMENT, DEST_SEGMENT,
SOURCE_SEGMENT_CONTOUR, DEST_SEGMENT_CONTOUR};
public:
virtual const T& getSegmentation(SegmentationType type) =0;
};
and derived a class
class B : public A<short*>
{
public:
const short*& getSegmentation(SegmentationType type);
};
from it. However, the compiler complains: "B::getSegmentation overriding virtual function return type differs and is not covariant from 'A::getSegmentation'". Since the template parameter T of class B is short* and the abstract method returns T&, I expected, that the concrete return value must be a reference to a short pointer, hence short*&. If, for testing purposes, I change the template parameter of B to short and the return value accordingly to short&, then everything works fine.
I would be thankful, if someone could tell me, why this doesn't work.
You need
short* const & getSegmentation(SegmentationType type);
// ^^^^^ const here
const T& is "reference to const T". Now if T is "pointer to short", then the return type needs to be "reference to const pointer to short", which is short* const &. const short * & is "reference to pointer to const short" - the constness applies to the pointed-to value, rather than the pointer.
The problem is that in B your function returns the reference to the pointer to const short while it should return the const reference to the pointer to short. Use
short* const& getSegmentation(SegmentationType type);
This should fix your issue:
short* const & getSegmentation(SegmentationType type);
In A you have a const reference to T, and in B you had a reference to const short, as it seems to me.
i just try to implement std::auto_ptr,my declaration is below.
template<typename T>
class auto_ptr_imp
{
public:
explicit auto_ptr_imp<T>(T const* rhs=NULL);
auto_ptr_imp(auto_ptr_imp<T>&);
auto_ptr_imp& operator =(auto_ptr_imp<T>&);
T& operator*();
T* operator->();
private:
T* m_ptr;
};
template<typename T>
auto_ptr_imp<T>::auto_ptr_imp(T * ptr)
{
this->m_ptr=ptr;
}
//in main function call this
auto_ptr_imp<ClassA>ptr(new ClassA(2)); //compile error
int*p=NULL;
int const* pp=p;//no error
then error: “=”: can not cast “const ClassA *”to“ClassA *”.
but i remember when assign a non-const pointer to a pointer to const ,implicit cast will happen. the code below complie well.this makes me confused.
I assume you meant to declare this:
explicit auto_ptr_imp<T>(T const* rhs=NULL);
As this:
explicit auto_ptr_imp<T>(T * const rhs=NULL);
Bear in mind that const T* and T const* are the same - a non-const pointer to const T. Only T * const is a const pointer to a non-const T.
If you could assign a value of type "pointer to const object" to a pointer to non-const object, then you could modify the const object through that pointer. Of course that's erroneous.
However, if you assign a pointer to non-const object to a pointer to const object, then you are just making a promise that despite the object being modifiable, you won't modify it. Mutation is not obligatory, so this is fine.