Russian Qt Forum

Программирование => С/C++ => Тема начата: Eugene Efremov от Май 12, 2007, 03:22



Название: operator[] via get/set
Отправлено: Eugene Efremov от Май 12, 2007, 03:22
Дано:

Код:

ValueType getItem(IndexType i);
void setItem(IndexType i, ValueType val);


Требуется:

Пользуясь этими ф-циями реализовать перегрузку оператора []:
Код:

ValueType& operator[](IndexType i);


Такое вообще возможно?

добавлено спустя 26 минут:

 Так... Я тормоз. Вот ответ:

Код:

class Mediator
{
   IndexType index;
public:
   Mediator(IndexType i):index(i)
   {}

   ValueType operator=(ValueType value)
   {
      setItem(index, value);
      return value;
   }
   
   operator ValueType()
   {
       return getItem(index);
   }
};

....

Mediator operator[](IndexType i)
{
   return Mediator(i);
}


В реальных примерах, конечно, Mediator будет содержать и ссылку на тот класс, которому эти геттеры/сеттеры принадлежат. Не говоря уже о том, что надо пользоваться const &... Но это все уже детали.

Задача решена, тему можно закрывать. ;-)


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 06, 2008, 20:53
Так его разтак... Гладко было на бумаге. :-E
Попробовал внедрить это дело в реальном проекте:

Код:
template<class cont_tp, class key_tp, class val_tp> class access_operator
{
cont_tp *data;
const key_tp& ind;
public:
access_operator(cont_tp *d, const key_tp& i):data(d),ind(i) {}

const val_tp& operator=(const val_tp& val)
{
data->set_item(ind, val);
return val;
}
operator const val_tp&() const
{
return data->get_item(ind);
}
};

И что получаем? Конструкции вида foo[ i].bar() не работают — в классе access_operator нет метода bar. И языковых средств, позволяющих объяснить компилятору, что в таком случае нужно преобразовывать его в val_tp — тоже нет: operator. не перегружается.

А если пару таких контейнеров вложить друг в друга, выяснится, что foo[ i][ j] тоже не работает...

А посему — вопрос снова в силе: Можно ли реализовать subj так, чтобы при этом ничего не отваливалось?


Название: Re: operator[] via get/set
Отправлено: Вячеслав от Апрель 06, 2008, 21:42
Абстрагируясь от вопроса 'а на кой это сдалось'
http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4031/ (http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4031/)
http://www.codeproject.com/KB/cpp/genericproperty.aspx (http://www.codeproject.com/KB/cpp/genericproperty.aspx)
http://forum.shelek.ru/index.php/topic,3887.0.html (http://forum.shelek.ru/index.php/topic,3887.0.html)
Но IMHO оно того не стоит ......


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 06, 2008, 22:54
Абстрагируясь от вопроса 'а на кой это сдалось'

В любой ситуации, когда мы не можем прочитать/записать объект напрямую, а должны что-то еще с ним сделать. Если он shared, нужно делать глубокое копирование перед изменением, если он хранится в где-то в сериализованном виде — его нужно "разархивировать". А может вообще никакого объекта нет и мы его создаем налету... Вариантов может быть много.

http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4031/ (http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4031/)
http://www.codeproject.com/KB/cpp/genericproperty.aspx (http://www.codeproject.com/KB/cpp/genericproperty.aspx)
http://forum.shelek.ru/index.php/topic,3887.0.html (http://forum.shelek.ru/index.php/topic,3887.0.html)

Спасибо, только там примерно то же самое, что и у меня, разве что более развернуто... Похоже, это единственный вменяемый способ реализовать такое в этом языке. :-/

Вообще, как я понял, проблема в том, что вызов метода — это не та операция, которую можно описать в терминах сеттеров/геттеров. Нужно добавить в интерфейс контейнера метод modify_item, возвращающий неконстантную ссылку. И юзать в этой ситуации именно его. А раз operator. не перегружается, придется вместо него юзать operator->. Криво, но, видимо, делать нечего...


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 07, 2008, 01:50
А вот для случая foo[ i][ j] (где foo[ i] — объект с перегруженным operator[]) я попрежнему хорошего решения не вижу. В текущем варианте приходится писать foo[ i]->operator[](j), что не есть рулез.

Так что вопрос «можно ли сделать лучше?» остается в силе...


Название: Re: operator[] via get/set
Отправлено: Tonal от Апрель 07, 2008, 08:07
Я бы сделал как минимум 2е версии класса - одну для простых объектов, вторую для массивов, с переопределённым operator[].
В этом случае запись foo[ i][ j] как раз канает.
Для доступа к членам - перегрузка operator* и operator->.

Так что получается 4 класса: для простых значений (int, char, float...), для массивов простых значений, для сложных значений (структуры, классы), для массивов сложных значений.

Хотя вопрос остаётся - зачем всё это громоздить, если есть get_item, set_item?
И кроме того, кроме индексации есть итераторы - может в их сторону стоит посмотреть? :-)

P.S. В принципе, вместо 4х отдельных шаблонов, всё это можно реализовать в одном конечном шаблоне используя type_traits и enable_if.


Название: Re: operator[] via get/set
Отправлено: Вячеслав от Апрель 07, 2008, 18:31
В любой ситуации, когда мы не можем прочитать/записать объект напрямую, а должны что-то еще с ним сделать. Если он shared, нужно делать глубокое копирование перед изменением, если он хранится в где-то в сериализованном виде — его нужно "разархивировать". А может вообще никакого объекта нет и мы его создаем налету... Вариантов может быть много.
Хм .... Это уже жабу напоминает .... или нечто скриптовое ..... Я пробывал в одном из проектов использовать эту методу - быстро забил - так-как слишком много геморря с поддержкой работоспособности конструкций :( Если глянуть в глубь и ширь - то кажись первопроходцем был Borland - в 5 BC (VDBT) там был какой-то дикий гибрид шаблонов\препроцессора,ну а далее BCB с расширениями языка и MS с их declspec .... IMHO пока это не введут на уровне языка стандартом - нуегонафик - геморру больно много :(
PS - а на vdbt наверное можно поглядеть - оно ведь работало ;)


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 08, 2008, 03:19
Хотя вопрос остаётся - зачем всё это громоздить, если есть get_item, set_item?

Ну конечно, это "синтаксический сахар", как и любая другая перегрузка операторов.
И разумеется, без него можно обойтись. Но с ним удобнее.

Я бы сделал как минимум 2е версии класса - одну для простых объектов, вторую для массивов, с переопределённым operator[].
В этом случае запись foo[ i][ j] как раз канает.
Для доступа к членам - перегрузка operator* и operator->.

Так что получается 4 класса: для простых значений (int, char, float...), для массивов простых значений, для сложных значений (структуры, классы), для массивов сложных значений.

Ммм... Или я не врубаюсь, или тут написано про что-то сильно другое.
У меня operator* отлично работает (gcc 3.4.5 и msvc 8, борланд с ваткомом пролетают — туда им и дорога) без перегрузки. С простыми значениями и их массивами никаких проблем нет. С указателями на классы тоже. Проблемы с вызовом методов классов имеются, но лечатся перегрузкой operator-> — и здесь действительно приходится писать специализацию для указателей, потому что иначе эта перегрузка их ломает.

Проблема только с классами, в которых переопределен operator[]. Переопределить его в общем случае проблематично, поскольку мы не знаем заранее тип его возвращаемого значения. Т.е. мы можем, допустим добавить в access_operator<cont_tp,key_tp,val_tp> такой метод:
Код:
template<class key_other> int& operator[](const key_other& k)
{
return (data->modify_item(ind))[k];
}
И если val_tp имеет operator[], возвращающий int& это будет работать. Но только в этом случае. А написать
Код:
template<class key_other, class val_other> val_other operator[](const key_other& k)
{
return (data->modify_item(ind))[k];
}
мы уже не можем. Точнее — можем, но смысла это иметь не будет, потому что по возвращаемому значению темплайты не распознаются, и никуда оно автоматически подставляться не будет.

Собственно, в этом и есть проблема. Если есть идеи, как заставить компилятор распознать это самое возвращаемое значение — буду рад их услышать...

P.S. В новой версии стандарта, которая, для таких вещей, кажется, есть ключевое слово auto. Но оно еще когда будет...


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 08, 2008, 04:12
Так, еще не уверен, но, кажется, нашел подходящее решение:
http://rsdn.ru/Forum/message/2683272.flat.aspx
Попробую использовать что-нибудь в этом роде...


Название: Re: operator[] via get/set
Отправлено: Eugene Efremov от Апрель 08, 2008, 06:45
Блин.... йя идиотъ... Достаточно потребовать, чтобы в контейнере был определен value_type с помощью typedef...
Остается, конечно, случай, когда вложенный контейнер "чужой". И вот для этого, как раз, и пригодятся извраты в стиле упомянутого auto_cast. Но это уже экзотика...


Название: Re: operator[] via get/set
Отправлено: Tonal от Апрель 08, 2008, 08:05
Ты же код не предоставил, поэтому я не в курсе, что ты уже реализовал и оно у тебя работает, а что нет. :)

Теперь вспоминаем о таких операторах как ++, += и чешем репу - так ли сладок этот сахар. :)
Кстати, в реализациях багланда и MSVC, свойства (расширения языка) не поддерживали эти операторы.

P.S. На rsdn-е довольно много обсуждались свойства на C++ - вроде даже статья есть...

P.P.S С моей точки зрения, свойства - несколько облегчают написание, но усложняют отладку и поддержку (с ходу не понятно к чему приведёт простое присвоение, и ломаются всяческие подсказки). Что перевесит - решать тебе, но я бы не стал извращаться. :)