Search code examples
c++const-correctness

Const objects with reference semantics


I have a class that the user uses to interface with a system. This class uses Pimpl to hide its internals, so its only actual member is a reference to the real, hidden object that does all the work.

Because the class has reference semantics, it's usually passed around by value much like a pointer. That leads to a problem with const correctness. You can break the const nature of the class very easily by simply copying a const value into a non-const value. And there's no way to avoid that than to prevent copying altogether.

I want to be able to return const values of these, which preserves the const nature of the object. Without creating a new class or something.

Basically I want to prevent this from working:

struct Ref
{
    int &t;
    Ref(int &_t) : t(_t) {}
};

Ref MakeRef(int &t) { return Ref(t); }

int main()
{
    int foo = 5;
    const Ref r(foo);
    const Ref c(r);            //This should be allowed.
    Ref other = MakeRef(foo);  //This should also be allowed.
    Ref bar(r);                //This should fail to compile somehow.

    return 0;
}

After all, it would fail to work if I did it directly:

int &MakeRef(int &t) {return t;}

int main()
{
    int foo = 5;
    const int &r(foo);
    const int &c(r);            //This compiles.
    int &other = MakeRef(foo);  //This compiles.
    int &bar(r);                //This fails to compile.

    return 0;
}

Solution

  • What you are asking is not possible. It's not possible for these 2 lines to behave differently:

    const Ref c(r);            //This should be allowed.
    Ref bar(r);                //This should fail to compile somehow.
    

    Both lines will execute through the same code path, they will both execute through the same copy constructor (yours or automatically generated). The only difference is the former will result in a const final variable.

    The unfortunately reality is that even if you managed to prevent the above from compiling in your desired case, someone could simply do the following to circumvent your protection:

    const Ref c(r);
    Ref &bar = (Ref&)c;
    

    If you're trying to prevent other people from doing nasty things to your class, you'll need to find an alternative to using a reference to a local variable. If you're just worried about yourself, then just don't do anything you shouldn't :)