Generally, you should only use operator overloading when it is natural. If you scratch a suitable operator for some functionality, then this is a good sign that you should not force the operator to overload your problem.
Having said that, you are trying to create a proxy object that sends read and write events to one of a pair of objects. A proxy object often overloads the -> operator to give pointer-like semantics. (You cannot overload.)
Although you can have two overloads -> , differentiated by const -ness, I would caution against this, since this is problematic for read operations. Overloading is selected by referencing an object through a constant or non-constant reference, and not whether the action is actually read or written. This fact makes error prone.
What you can do is split access from the repository and create a multi-buffer class template and a buffer access template that accesses the corresponding member using operator-> for syntactic simplicity.
This class stores several instances of the template parameter T and stores the offset so that different accessors can retrieve the front / active buffer or other buffers by relative offset. Using the template parameter n == 1 means that there is only one instance of T , and multi-buffering is effectively disabled.
template< class T, std::size_t n > struct MultiBuffer { MultiBuffer() : _active_offset(0) {} void ChangeBuffers() { ++_active_offset; } T* GetInstance(std::size_t k) { return &_objects[ (_active_offset + k) % n ]; } private: T _objects[n]; std::size_t _active_offset; };
This class abstracts the choice of buffer. It refers to the MultiBuffer through a link, so you must ensure that its service life is shorter than the MultiBuffer that it uses. It has its own offset, which is added to the MultiBuffer offset, so different BufferAccess can refer to different members of the array (for example, template parameter n = 0 for access to the front buffer and 1 for access to the back buffer).
Note that the BufferAccess offset is a member, not a template parameter, so the methods that work with BufferAccess objects BufferAccess not bound to only one specific offset, or must be templates themselves. I made the count object a template parameter, because from your description it is most likely a configuration parameter, and this gives the compiler the maximum opportunity for optimization.
template< class T, std::size_t n > class BufferAccess { public: BufferAccess( MultiBuffer< T, n >& buf, std::size_t offset ) : _buffer(buf), _offset(offset) { } T* operator->() const { return _buffer.GetInstance(_offset); } private: MultiBuffer< T, n >& _buffer; const std::size_t _offset; };
When compiling all this together with the test class, note that when overloading -> we can easily call the members of the test class from an instance of BufferAccess without BufferAccess , which needs some knowledge of what the members are.
In addition, no change is made between single and double buffering. Triple buffering is also trivial if you can find it.
class TestClass { public: TestClass() : _n(0) {} int get() const { return _n; } void set(int n) { _n = n; } private: int _n; }; #include <iostream> #include <ostream> int main() { const std::size_t buffers = 2; MultiBuffer<TestClass, buffers> mbuf; BufferAccess<TestClass, buffers> frontBuffer(mbuf, 0); BufferAccess<TestClass, buffers> backBuffer(mbuf, 1); std::cout << "set front to 5\n"; frontBuffer->set(5); std::cout << "back = " << backBuffer->get() << '\n'; std::cout << "swap buffers\n"; ++mbuf.offset; std::cout << "set front to 10\n"; frontBuffer->set(10); std::cout << "back = " << backBuffer->get() << '\n'; std::cout << "front = " << frontBuffer->get() << '\n'; return 0; }