Russian Qt Forum
Ноябрь 23, 2024, 04:25 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: operator[] via get/set  (Прочитано 8981 раз)
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 &... Но это все уже детали.

Задача решена, тему можно закрывать. ;-)
Записан
Eugene Efremov
Гость
« Ответ #1 : Апрель 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 так, чтобы при этом ничего не отваливалось?
« Последнее редактирование: Апрель 06, 2008, 21:41 от Eugene Efremov » Записан
Вячеслав
Гость
« Ответ #2 : Апрель 06, 2008, 21:42 »

Абстрагируясь от вопроса 'а на кой это сдалось'
http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4031/
http://www.codeproject.com/KB/cpp/genericproperty.aspx
http://forum.shelek.ru/index.php/topic,3887.0.html
Но IMHO оно того не стоит ......
Записан
Eugene Efremov
Гость
« Ответ #3 : Апрель 06, 2008, 22:54 »

Абстрагируясь от вопроса 'а на кой это сдалось'

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


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

Вообще, как я понял, проблема в том, что вызов метода — это не та операция, которую можно описать в терминах сеттеров/геттеров. Нужно добавить в интерфейс контейнера метод modify_item, возвращающий неконстантную ссылку. И юзать в этой ситуации именно его. А раз operator. не перегружается, придется вместо него юзать operator->. Криво, но, видимо, делать нечего...
Записан
Eugene Efremov
Гость
« Ответ #4 : Апрель 07, 2008, 01:50 »

А вот для случая foo[ i][ j] (где foo[ i] — объект с перегруженным operator[]) я попрежнему хорошего решения не вижу. В текущем варианте приходится писать foo[ i]->operator[](j), что не есть рулез.

Так что вопрос «можно ли сделать лучше?» остается в силе...
Записан
Tonal
Гость
« Ответ #5 : Апрель 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.
Записан
Вячеслав
Гость
« Ответ #6 : Апрель 07, 2008, 18:31 »

В любой ситуации, когда мы не можем прочитать/записать объект напрямую, а должны что-то еще с ним сделать. Если он shared, нужно делать глубокое копирование перед изменением, если он хранится в где-то в сериализованном виде — его нужно "разархивировать". А может вообще никакого объекта нет и мы его создаем налету... Вариантов может быть много.
Хм .... Это уже жабу напоминает .... или нечто скриптовое ..... Я пробывал в одном из проектов использовать эту методу - быстро забил - так-как слишком много геморря с поддержкой работоспособности конструкций Грустный Если глянуть в глубь и ширь - то кажись первопроходцем был Borland - в 5 BC (VDBT) там был какой-то дикий гибрид шаблонов\препроцессора,ну а далее BCB с расширениями языка и MS с их declspec .... IMHO пока это не введут на уровне языка стандартом - нуегонафик - геморру больно много Грустный
PS - а на vdbt наверное можно поглядеть - оно ведь работало Подмигивающий
Записан
Eugene Efremov
Гость
« Ответ #7 : Апрель 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. Но оно еще когда будет...
Записан
Eugene Efremov
Гость
« Ответ #8 : Апрель 08, 2008, 04:12 »

Так, еще не уверен, но, кажется, нашел подходящее решение:
http://rsdn.ru/Forum/message/2683272.flat.aspx
Попробую использовать что-нибудь в этом роде...
Записан
Eugene Efremov
Гость
« Ответ #9 : Апрель 08, 2008, 06:45 »

Блин.... йя идиотъ... Достаточно потребовать, чтобы в контейнере был определен value_type с помощью typedef...
Остается, конечно, случай, когда вложенный контейнер "чужой". И вот для этого, как раз, и пригодятся извраты в стиле упомянутого auto_cast. Но это уже экзотика...
Записан
Tonal
Гость
« Ответ #10 : Апрель 08, 2008, 08:05 »

Ты же код не предоставил, поэтому я не в курсе, что ты уже реализовал и оно у тебя работает, а что нет. Улыбающийся

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

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

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


Страница сгенерирована за 0.184 секунд. Запросов: 20.