Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent reassignment of a reference?

Consider that in some library somewhere (which we have no access to change), we have a Counter class:

class Counter {
    int count;
  public:
    Counter() : count(0) { }
    void bump() { ++count; }
    int getCount() const { return count; }
};

which, by its very nature, is mutable. If it's const, it's pretty worthless.

And in our code, we "use" that Counter. Badly.

#include <string>
#include <iostream>
#include <Counter.hpp>

using std::cout;
using std::endl;

void breakTheHellOutOfCounter(Counter &c) {
    // This is OK
    c.bump();

    // Oh noes!
    c = Counter();
}

int main() {
    Counter c;
    c.bump(); c.bump(); c.bump();
    std::cout << "Count was " << c.getCount() << std::endl;
    breakTheHellOutOfCounter(c);
    std::cout << "Count is now " << c.getCount() << std::endl;
}

Note that breakTheHellOutOfCounter overwrites main's counter with a shiny new one, resetting the count. That's going to cause the caller some grief. (Imagine something a lot more harmful happening, and you'll see where I'm going here.)

I need to be able to bump c (and thus, I need it mutable), but I want breakTheHellOutOfCounter() to fail miserably due to trying to replace c. Is there a way I can change things (other than the Counter class) to make that happen?

(I'm aware that at the lowest levels, this is all but impossible to enforce. What I want is a way to make it hard to do accidentally.)

like image 966
cHao Avatar asked Dec 27 '22 20:12

cHao


1 Answers

The cleanest solution I can see to this without modifying counter itself is something like:

#include <string>
#include <iostream>
#include <Counter.hpp>

template <typename T>
struct Unbreakable : public T {
  Unbreakable<T>& operator=(const Unbreakable<T>&) = delete;
  Unbreakable<T>& operator=(Unbreakable<T>&&) = delete;

  template <typename ...Args>
  Unbreakable(Args&& ...args) : T(std::forward<Args>(args)...) {}
};

using std::cout;
using std::endl;

void breakTheHellOutOfCounter(Unbreakable<Counter> &c) {
    // this is ok
    c.bump();

    // oh noes!
    c = Counter();
}


int main() {
    Unbreakable<Counter> c;
    c.bump(); c.bump(); c.bump();
    std::cout << "Count was " << c.getCount() << std::endl;
    breakTheHellOutOfCounter(c);
    std::cout << "Count is now " << c.getCount() << std::endl;
}

Which correctly gives an error from your "oh noes" line. (Example uses C++11, but C++98 solution is similar)

That doesn't rule out usage like:

Counter& br = c;
br = Counter();

of course, but without modifying Counter itself I don't think that's avoidable.

like image 53
Flexo Avatar answered Jan 10 '23 18:01

Flexo