Arnaud
Arnaud

Reputation: 223

A delicate issue about const qualifier when objects are referenced by other objects

Imagine that I have an object A that refers to an object B through its address. The address to B is given in the constructor of A. Some methods of A modify B, other do not.

Here is a very simple illustration :

class B {
public :
void f1() const;
void f2();
};

class A {
public :
 A(B * pb) {m_pb=pb;}
 void g1() {m_pb->f1();} // g1 do not modify *m_pb
 void g2() {m_pb->f2();} // g2 do modify *m_pb
private :
  B * m_pb;
}

Now, imagine that in some part of my code, I have a variable whose type is const B &. I want to create an object A to call the g1 method that do not modify the object B. But I cannot construct an object A at all.

One solution would be to create 2 classes A1 and A2 with A1 referencing a const B * and defining only method g1 while A2 would reference a B * and would define both g1 and g2. (g1 would be defined in 2 different places). I don't find this solution very elegant.

I wonder whether there is a way to explain to the compilator that : const A won't modify the object B that it is referring.So a const A objet can be constructed using a const B object.

Upvotes: 1

Views: 63

Answers (1)

Quimby
Quimby

Reputation: 19113

I wonder whether there is a way to explain to the compilator that : const A won't modify the object B that it is referring.So a const A objet can be constructed using a const B object.

What you want is const constructor and there isn't one. But you can avoid duplicating the interface with inheritance:

struct B{
    void f1() const{}
    void f2() {}
};

class constA
{
public:
    constA(const B* b):b(b){}
    // Should be const as it does not changes `constA` object.
    void g1() const {b->f1();}

    // No harm in this, but can be protected if you want
    const B* getB() const { return b;}
private:
    const B* b;
};

class A : public constA
{
public:
    A(B* b):constA(b){}
    void g2() const { getMutableB()->f2();}
private:
    B* getMutableB() const { return const_cast<B*>(getB());}
};

int main()
{
   B b;
   const B cb;

   A a(&b);
   a.g2();
   a.g1();

   constA ca(&cb);
   ca.g1();
   //ca.g2();

   constA ca2(&b);
   ca.g1();
   //ca.g2();

}

EDIT: (Per request from @formerlyknownas_463035818 with which I agree ) Quick refresher on const_cast:

int main()
{
    const int x = 5;
    int y = 5;
    const int* c_ptr = &x; 
    //'ptr' is valid object with known value.
    int* ptr = const_cast<int*>(c_ptr);
    // Is valid because '*ptr' is not modified
    int value = *ptr;
    // Undefined behaviour because '*ptr' is const object.
    *ptr = 5;

    const int* c_ptr2 = &y; 
    //'ptr2' is valid object with known value.
    int* ptr2 = const_cast<int*>(c_ptr2);
    // Is valid because '*ptr2' is not modified
    int value = *ptr2;
    // Is valid because '*ptr2' is not a const object.
    *ptr2 = 5;
}

Given that A only has non-const B constructor then getB() always returns a pointer to a object that is not const. So the second part of the above example applies and everything is safe.

Note that const casting const B in order to pass it to A would produce UB when calling g2 but that is true for all B* ptrs with unknown origin and there's nothing A class can do about it.

Upvotes: 2

Related Questions