Как правило, вы должны использовать перегрузку оператора только тогда, когда это естественно. Если вы ищете подходящего оператора для какой-то функциональности, это хороший знак того, что вам не следует заставлять оператора перегружать свою проблему.
Сказав это, вы пытаетесь создать прокси-объект, который отправляет события чтения и записи одному из пары объектов. Прокси-объект часто перегружает оператор ->
для получения семантики, подобной указателю. (Вы не можете перегрузить .
.)
Хотя вы можете иметь две перегрузки ->
, дифференцированные по const
-ness, я бы предостерег от этого, так как это затрудняет чтение. Перегрузка выбирается тем, на какой объект ссылается постоянная или неконстантная ссылка, а не тем, является ли действие действительным чтением или записью. Этот факт делает ошибку подхода подходящей.
Что вы можете сделать, это разделить доступ из хранилища и создать шаблон мультибуферного класса и шаблон буфера доступа, который обращается к соответствующему члену, используя operator->
для упрощения синтаксиса.
Этот класс хранит несколько экземпляров параметра шаблона T
и сохраняет смещение, чтобы различные методы доступа могли извлечь передний / активный буфер или другие буферы с помощью относительного смещения. Использование параметра шаблона n == 1
означает, что существует только один экземпляр T
, и мультибуферизация фактически отключена.
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;
};
Этот класс абстрагирует выбор буфера. Он ссылается на MultiBuffer
по ссылке, поэтому вы должны гарантировать, что его срок службы меньше, чем MultiBuffer
, который он использует. Он имеет собственное смещение, которое добавляется к смещению MultiBuffer
, так что разные BufferAccess
могут ссылаться на разные элементы массива (например, параметр шаблона n = 0 для доступа к переднему буферу и 1 для доступа к заднему буферу).
Обратите внимание, что смещение BufferAccess
является элементом, а не параметром шаблона, поэтому методы, которые работают с BufferAccess
объектами, не привязаны к работе только с одним конкретным смещением или должны быть самими шаблонами. Я сделал подсчет объектов параметром шаблона, так как, по вашему описанию, это скорее всего вариант конфигурации, и это дает компилятору максимальную возможность для оптимизации.
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;
};
Соединяя все это с тестовым классом, обратите внимание, что, перегружая ->
, мы можем легко вызывать члены тестового класса из экземпляра BufferAccess
без необходимости BufferAccess
знать, какие члены у тестового класса .
Также не одно изменение переключается между одиночной и двойной буферизацией. Тройная буферизация также тривиальна, если вы можете найти в ней необходимость.
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;
}