Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Const correctness with objects containing shared_ptr

Consider the object:

class Obj
{
    public:
        Obj() : val(new int(1)) {}
        int& get() {return *val;}
        const int& get() const {return *val;}

    private:
        std::shared_ptr<int> val;
};

As expected, when the object is constructed and copies are made they can all modify the same value through the shared_ptr exposed by Obj.

    Obj nonconst1;
    Obj nonconst2(nonconst1);
    nonconst2.get() = 2;
    cout << nonconst1.get() << ", " << nonconst2.get() << endl;

It's also possible to copy-construct a const Obj object from one of the non const, which seems to do the correct thing in that it allows reading but not writing to the value - as expected the following code results in a compile error:

    const Obj const1(nonconst1);
    const1.get() = 3;

However it is possible to copy-construct a non-const Obj from the const one, which then does permit the value to be modified.

    Obj nonconst3(const1);
    nonconst3.get() = 3;

To me this doesn't feel const-correct.

Is there a way to prevent this behaviour, whilst still allowing the copy constructor to work? In my real use case, I still want std containers of Obj to be possible.

like image 914
tangobravo Avatar asked Apr 30 '13 08:04

tangobravo


2 Answers

No, there isn't, unless you want to store a shared_ptr<const int>, in which case nobody can access it as non-const.

like image 21
Puppy Avatar answered Sep 28 '22 17:09

Puppy


"To me this doesn't feel const-correct" yet it is: you're simply invoking a non const get method on a non const Obj. Nothing wrong with that.

If you really need the behaviour you're after, you could to use something like a const proxy to Obj but then your clients must be able to handle it of course:

class Obj
{
  //...
  //original class definition goes here
  //...
  friend class ConstObj;
};  

class ConstObj
{
  public:
    ConstObj( const Obj& v ) : val( v.val ) {}
    const int& get() const { return *val; }

   private:
    std::shared_ptr<int> val;
};

//usage:
class WorkingWithObj
{
public:
  WorkingWithObj();
  Obj DoSomethingYieldingNonConstObj();
  ConstObj DoSomethingYieldingConstObj();
};

WorkingWithObj w;
Obj nonconst( w.DoSomethingYieldingNonConstObj() );
nonconst.get() = 3;

ConstObj veryconst( nonconst );
veryconst.get() = 3; //compiler error

ConstObj alsoconst( w.DoSomethingYieldingConstObj() );
alsoconst.get() = 3; //compiler error
like image 119
stijn Avatar answered Sep 28 '22 17:09

stijn