c++referencenoncopyable

Reassigning to struct that contains a reference


I'm trying to make a class that's a handler for messaging with a certain state. When a message is prepared, I expect to create a new state_t struct referring to the new y data. I want to then be able to access the new object via the same state variable. A minimal example with pointers would be something like this:

class Handler {
  int x;  // something config for the handler
  struct state_t {
    int *y;
    state_t(int *y) : y(y) {}
  };

  std::optional<state_t> state;

  Handler(int x) : x(x), state(std::nullopt) {}

  void prepare_message(int *y) {
    state = state_t(y);
  }

  void handle_state() {
    // do something with the y e.g. send it over a socket
  }

};

I was wondering if there's a good way to make this work with a reference instead of a pointer to y. Replacing it directly wouldn't compile because copy assignment is implicitly removed. Can this be overcome in an elegant way?


Solution

  • As @RemyLebeau commented, you can use std::reference_wrapper.
    As you can see it does exactly what you require:

    std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object.

    This is demonstrated below.

    Note that I added a public test() method to show that state_t is now copyable/assignable (although the fact that prepare_message compiles also implies that).
    I also made the constructor of Handler public in order to create it from main.

    #include <optional>
    #include <iostream>
    #include <functional>
    
    class Handler {
        [[maybe_unused]] int x;  // something config for the handler
        struct state_t {
    //------vvvvvvvvvvvvvvvvvvvvvv--------
            std::reference_wrapper<int> y;
            state_t(int & y) : y(y) {}
        };
    
        std::optional<state_t> state;
    
        void prepare_message(int & y) {
            state = state_t(y);
        }
    
        void handle_state() {
        }
    
    public:
        Handler(int x) : x(x), state(std::nullopt) {}
        
        // Demonstration that `state_t` is now copyable:
        void test() {
            int x1 = 1;
            int x2 = 2;
            state_t s1{ x1 };
            state_t s2{ x2 };
            std::cout << s1.y << "\n";
            s1 = s2;
            std::cout << s1.y << "\n";
        }
    };
    
    int main() {
        Handler h{ 5 };
        h.test();
    }
    

    Output:

    1
    2
    

    Live demo