Reputation: 363
In my custom class, I'm getting an error when declaring the input variable pointer as constant and copying it into a non-constant member variable pointer.
The error I get is:
Cannot initialize a member subobject of type 'Position *' with an lvalue of type 'const Position *'
Why does this happen? And why is this not consistent with the other member variable (_attack
)? It is only copying the value of the pointer (address) the same way it's copying the value of the int (_attack
).
class Creature
{
private:
int _attack;
Position* _position;
public:
Creature(const int attack, const Position* position)
: _attack{attack}, _position{position} // ERROR: Cannot initialize a member subobject of type 'Position *' with an lvalue of type 'const Position *'
{}
};
Upvotes: 2
Views: 1597
Reputation: 180500
Pointers have two "types" of const
. They are
const T * pointer_name
and
T * const pointer_name
which can also be combined to the third and final form of
const T * const pointer_name
With the first one, you are making a pointer to a const T
. This is what you have in constructor. This goes against the type of _position
, which is T *
, or more to the point, a pointer to a non-const T
. This is why you are getting an error. You can't assign to a pointer to non-const T
the value of a pointer to a const T
.
With the second form, your code could compile as now you don't have a pointer to a const T
, but instead you have a const
pointer to a T
. Both pointer point to a non-const thing, so it's okay to assign one to the other
This is the same thing that is happening with attack
. While it may be const, you can still copy it's value and place it into another object.
The last form is a const
pointer to a const T
, and wouldn't work here again for the same reason as why the first from doesn't work.
This also kind of gets into the east-coast vs west-coast const-style. With west-coast style above, sometimes it's not so easy to understand the type. Insteasd, you can use east-coast style like
T const * pointer_name
T * const pointer_name
T const * const pointer_name
and now they all read from right to left exactly what the type is.
Upvotes: 1
Reputation: 51825
In the case of the attack
parameter to your constructor (declared as const
), you are copying the value given to your member variable (_attack
), which is not const
; however, subsequent modification of that member variable will not change the value of the passed parameter, so you aren't "breaking the promise you made."
However, in the case of the position
parameter, things are very different: the const
qualifier here states that it points to an object that cannot be modified; but, if you were to copy that pointer's value (i.e. the address) to the non-const member variable, then you would be allowing modification of the referenced object. So, the compiler is (correctly) telling you can't do that.
If what you actually want is to pass a pointer that is an immutable address, but to an object that can be changed, then you need to move the const
keyword in the constructor's parameters:
Creature(const int attack, Position* const position) // POINTER is const, POINTEE is not!
Upvotes: 1
Reputation: 1852
The first parameter does not refer to anything outside of the constructor scope as it is just a copy. The const in that case is only saying that your constructor itself is not allowed to change the value of attack
.
The second parameter is a pointer to a const Position
. If you could assign that to a non-const Position*
, you could then mutate that position through the non-const pointer. If this was allowed, there would be no way for someone to create an instance of your class if they have a Position pointer they did not want mutated by your constructor.
Note that you can make the second parameter behave the same as the first by changing it to Position* const position
. This makes the pointer const but not the object it is pointing to.
See this related question.
Upvotes: 1