2009-09-06 2 views
6

Я пытаюсь создать «разреженный» вектор класс в C++, например, так:Перегрузка оператора [] для разреженного вектора

template<typename V, V Default> 
class SparseVector { 
    ... 
} 

Внутренне, он будет представлен в std::map<int, V> (где V является тип сохраненного значения). Если элемент отсутствует в карте, мы будем делать вид, что он равен значению Default из аргумента шаблона.

Однако у меня возникли проблемы с перегрузкой оператора индекса, []. Я должен перегрузить оператор [], потому что я передаю объекты из этого класса в функцию Boost, которая ожидает, что [] будет работать правильно.

Версия const достаточно проста: проверьте, находится ли индекс на карте, верните его значение, если это так, или Default в противном случае.

Однако неконвертная версия требует, чтобы я возвращал ссылку, и именно там я столкнулся с проблемой. Если значение только , прочитайте, мне не нужно (и не хочу) добавлять что-либо к карте; но если это написано, мне, возможно, нужно будет ввести новую запись на карту. Проблема в том, что перегруженный [] не знает, находится ли значение , или , написанное. Он просто возвращает ссылку.

Есть ли способ решить эту проблему? Или, возможно, обойти это?

+2

boost :: mapped_vector <> должен делать что-то похожее - вы можете изучить его для идей (или, может быть, просто использовать его). –

+0

Он не поддерживает мои значения по умолчанию, а также я собирался сделать это для двумерной матрицы, поэтому использовать его прямо не может быть и речи. Но все же полезная ссылка! – Thomas

ответ

13

Может быть какой-то очень простой трюк, но в остальном я думаю, что operator[] должен вернуть только то, что может быть назначено из V (и преобразовано в V), не обязательно V &. Поэтому я думаю, вам нужно вернуть какой-то объект с перегруженным operator=(const V&), который создает запись в вашем разреженном контейнере.

Вам нужно будет проверить, что делает функция Boost с параметром шаблона, однако - преобразование, определяемое пользователем, на V влияет на то, какие цепочки преобразования возможны, например, предотвращая наличие более определенных пользователем преобразований в одном и том же цепь.

+0

Это единственное решение. С другой стороны, объекты-прокси также не идеальны - например, если 'V' имеет какие-либо перегруженные операторы преобразования, прокси-сервер не сможет беспрепятственно их распространять. –

+0

А как раз протестировали векторы повышения. Похоже, они тоже так себя ведут. Интересно. –

+0

(И 'vector ' также не является контейнером STL.) – sbi

9

Не допускайте, чтобы оператор non-const & возвращал ссылку, но прокси-объект. Затем вы можете реализовать оператор присваивания прокси-объекта, чтобы отличать обращения чтения к оператору [] от доступа к записи.

Вот несколько эскизов кода, чтобы проиллюстрировать идею. Этот подход не очень хорош, но хорошо - это C++. Программисты на С ++ не теряют время, конкурируя в конкурсах красоты (у них тоже не будет шансов). ;-)

template <typename V, V Default> 
ProxyObject SparseVector::operator[](int i) { 
    // At this point, we don't know whether operator[] was called, so we return 
    // a proxy object and defer the decision until later 
    return ProxyObject<V, Default>(this, i); 
} 

template <typename V, V Default> 
class ProxyObject { 
    ProxyObject(SparseVector<V, Default> *v, int idx); 
    ProxyObject<V, Default> &operator=(const V &v) { 
     // If we get here, we know that operator[] was called to perform a write access, 
     // so we can insert an item in the vector if needed 
    } 

    operator V() { 
     // If we get here, we know that operator[] was called to perform a read access, 
     // so we can simply return the existing object 
    } 
}; 
+0

Можно ли каким-либо образом преобразовать тип, например. как 'const int &', так и 'int &', и компилятор выбирает первый, когда он может использовать незаписываемое значение lvalue? Я хотел бы, чтобы объект-прокси возвращал ссылку на поле, которое загружается из основного объекта, а затем его деструктор копирует поле обратно к основному объекту, если он может быть изменен, но я не могу понять, как чтобы избежать обратной записи на доступ только для чтения. – supercat

1

Интересно, звучит ли этот дизайн.

Если вы хотите вернуть ссылку, это означает, что клиенты класса могут хранить результат вызова operator[] в ссылке и читать от него/писать к нему в любое другое время. Если вы не возвращаете ссылку и/или не вставляете элемент при каждом конкретном указателе, как они могут это сделать? (Кроме того, у меня есть ощущение, что для стандартного требования требуется подходящий контейнер STL, предоставляющий operator[], чтобы оператор возвращал ссылку, но я не уверен в этом.)

Возможно, вы сможете обойти это, предоставив свой прокси также operator V&() (который создаст запись и присвоит значение по умолчанию), но я не уверен, что это не просто откроет другое отверстие в петле в каком-то случае Я еще не думал.

std::map решает эту проблему, указав, что неконстантная версия этого оператора всегда вставляет элемент (и вообще не предоставляет версию const).

Конечно, вы всегда можете сказать это не готовый контейнер STL, а operator[] не возвращает простые ссылки, которые пользователи могут хранить. И, возможно, все в порядке. Мне просто интересно.

+0

О прочности конструкции: это оптимизация памяти общего вектора, который будет использоваться в конкретных случаях - так что интерфейс/контракт/документация должны охватывать это. О правильности ссылок: даже stl-итераторы не всегда действительны. – xtofl

+2

@xtofl: однако контейнеры STL очень тщательно определяют, какие операции могут аннулировать итераторы и/или ссылки на элементы контейнера. Этот контейнер должен делать то же самое, и пользователи должны убедиться, что любые шаблоны, использующие класс в качестве параметра, не требуют требований, которые он не может удовлетворить. –

Смежные вопросы