Michel H
Michel H

Reputation: 363

C++: Copying pointer to const object into non-const object pointer

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

Answers (3)

NathanOliver
NathanOliver

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

Adrian Mole
Adrian Mole

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

Dean Johnson
Dean Johnson

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

Related Questions