Re[16]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 25.05.04 10:41
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>Нет не обиделся, просто не могу сказать, что мне это приятно...


Ну тогда извини.

RL>Да действительно те книги которые прочитал буквально вскользь затрагивали тему этой дискуссии.

RL>Сейчас по почте закаказал книгу Страуструпа, буду грызть его

+1

RL>Весь смысл задаваемых мной вопросов сводится к пониманию следующего кода:

RL>(из книги Дейла Роджерсона "Основы технологии COM").

уууу! Так ты с Си на СОМ пересел? Смело

Разборки вырезаны. -- ПК
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[17]: Перегрузка операторов
От: Red Line  
Дата: 25.05.04 11:02
Оценка:
Здравствуйте, adontz, Вы писали:

A>уууу! Так ты с Си на СОМ пересел? Смело


Это прямо в точку
Так и есть
Все вопросы связанные с мнжественным наследованием и виртальными функцими мне понятны
(качнул книгу "С++ бархатный путь" избранные главы).
Меня тормозят следующие моменты:

// Преобразование
operator T*() { return m_pI; }
// Операции с указателем
T& operator*() { assert(m_pI != NULL); return *m_pI; }
T** operator&() { assert(m_pI == NULL); return &m_pI; }-------- > только этот понятен
T* operator->() { assert(m_pI != NULL); return m_pI; }

Удалено избыточное цитирование. -- ПК
Re[18]: Перегрузка операторов
От: Кодт Россия  
Дата: 25.05.04 11:20
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>Меня тормозят следующие моменты:


Рассмотрим интерфейс ISome и смарт-птр к нему, CComPtr<ISome> == CSomePtr;

RL>// Преобразование
RL>operator T*() { return m_pI; }

CSomePtr ptr;
void foo(ISome* p);

ISome* p = ptr;
foo(ptr);

RL>// Операции с указателем
RL>T& operator*() { assert(m_pI != NULL); return *m_pI; }

// эквивалентные способы вызвать метод: оператор-> и разыменовать...
((ISome*)ptr)->method();
(*((ISome*)ptr)).method();
(*ptr).method();

RL>T** operator&() { assert(m_pI == NULL); return &m_pI; }-------- > только этот понятен

// Кстати, довольно зловредный оператор. В ряде случаев мешает.
// Лучше уж ввести в CSomePtr метод reference(), возвращающий ссылку на переменную-член

RL>T* operator->() { assert(m_pI != NULL); return m_pI; }

// оператор доступа
((ISome*)ptr)->method();
ptr->method();

Оператор -> — занятная фича языка С++. В выражении obj->member выполняется следующее:
result_of_arrow_1 t1 = obj.operator->();
// если result_of_arrow_1 - не указатель, то
result_of_arrow_2 t2 = t1.operator->();
// если опять не указатель, то
result_of_arrow_3 t3 = t2.operator->();
...
// наконец, tN - это указатель
(*tN).member
Перекуём баги на фичи!
Re[9]: Перегрузка операторов
От: Red Line  
Дата: 25.05.04 12:50
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Посмотрим, как выглядят операторы-члены.


Покажите пожалуйста пример использования оператора (члена класса) объявленного так:

1) инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) — это сам объект, а правый указывается в сигнатуре. Сравни:
class C
{
public:
    // функциональная форма
  static C add(const C& lhs, const C& rhs);  

  C operator + (const C& rhs) const { return add( *this, rhs ); }
};


Удалено избыточное цитирование. Исправлено форматирование. -- ПК
Re[10]: Перегрузка операторов
От: jazzer Россия Skype: enerjazzer
Дата: 25.05.04 13:05
Оценка:
Здравствуйте, Red Line, Вы писали:


RL>Покажите пожалуйста пример использования оператора (члена класса) объявленного так:


RL>

RL>Посмотрим, как выглядят операторы-члены.
RL>1) инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) - это сам объект, а правый указывается в сигнатуре. Сравни:
RL>class C
RL>{
RL>public:
RL>    // функциональная форма
RL>  static C add(const C& lhs, const C& rhs);  

RL>  C operator + (const C& rhs) const { return add( *this, rhs ); }
RL>};
RL>

Вот так:
С a, b;
a + b; // эквивалентно a.operator+(b);

P.S. Не оверквоть :)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[19]: Перегрузка операторов
От: Red Line  
Дата: 25.05.04 13:34
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Red Line, Вы писали:


RL>>Меня тормозят следующие моменты:


К>Рассмотрим интерфейс ISome и смарт-птр к нему, CComPtr<ISome> == CSomePtr;


К>
RL>>// Преобразование
RL>>operator T*() { return m_pI; }

К>CSomePtr ptr;
К>void foo(ISome* p);

К>ISome* p = ptr;
К>foo(ptr);

RL>>// Операции с указателем
RL>>T& operator*() { assert(m_pI != NULL); return *m_pI; }

К>// эквивалентные способы вызвать метод: оператор-> и разыменовать...
К>((ISome*)ptr)->method();
К>(*((ISome*)ptr)).method();
К>(*ptr).method();

RL>>T** operator&() { assert(m_pI == NULL); return &m_pI; }-------- > только этот понятен

К>// Кстати, довольно зловредный оператор. В ряде случаев мешает.
К>// Лучше уж ввести в CSomePtr метод reference(), возвращающий ссылку на переменную-член

RL>>T* operator->() { assert(m_pI != NULL); return m_pI; }

К>// оператор доступа
К>((ISome*)ptr)->method();
ptr->>method();
К>

К>Оператор -> — занятная фича языка С++. В выражении obj->member выполняется следующее:
К>
К>result_of_arrow_1 t1 = obj.operator->();
К>// если result_of_arrow_1 - не указатель, то
К>result_of_arrow_2 t2 = t1.operator->();
К>// если опять не указатель, то
К>result_of_arrow_3 t3 = t2.operator->();
К>...
К>// наконец, tN - это указатель
К>(*tN).member
К>



// Операции с указателем
RL>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
Я ни как не вразумею, — как нужно читать синтаксис этого оператора.

Мне понятна запись:

С operator + (const C lPar, const C rPar);


Читаю (в соответствии с тем как Вы меня учили)
Внешний оператор;
двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);

Возможны две формы вызова
явная:

C x,y,z;

z = +(x,y);

и операторная:

С x,y,z;

z = x+y;

Верно?

Теперь оператор-член класса:


 class С
{
  C add(val1, val2);
  C operator + (C rPar) const {return add(*this, rPar);}
};


Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)
двухместный (бинарный) и вроде бы инфиксный;

Явная форма вызова:

С obj;
C anyPar;
C res;
res = obj.operator + (anyPar); //равносильно obj+anyPar? Т.е текущий объект складывается с параметром, и что будет блин не догоняю ((

Операторная форма:

С obj;
C anyPar;
C res;

res = +anyPar;// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????


Мне бы хотелось в такой же форме разобрать и эти операторы:


 class cPtr
{
   private:
   T* p;//указатель на искомый интерфейс
   public:
   operator T*() {  return m_pI; }
   T& operator*() { return *m_pI; }
   T** operator&() { return &m_pI; }
   T* operator->() { return m_pI; }
};


Прошу извинить если надоедаю
Re[20]: Перегрузка операторов
От: Кодт Россия  
Дата: 25.05.04 14:37
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>// Операции с указателем
RL>>>T& operator*() { assert(m_pI != NULL); return *m_pI; }

RL>Я ни как не вразумею, — как нужно читать синтаксис этого оператора.

Проще некуда:
struct Data
{
  int x;
  void foo();
};

// некоторые функции...
void accept_data(Data d); // входной параметр
void fill_data(Data& d); // выходной параметр


struct SmartPtr
{
  Data* p;

  Data& operator* () { return *p; }
};

main()
{
  Data* p1;
  SmartPtr p2;
  . . . . .

  // обычный указатель      // явный доступ к члену умного     // то же самое,
                            // -- обычному указателю           // но завёрнутое в оператор разыменования
  fill_data(*p1);           fill_data(*(p2.p));                fill_data(*p2);
  accept_data(*p1);         accept_data(*(p2.p));              accept_data(*p2);
}

Видишь, что внешне работа с обычным указателем (левый столбец) выглядит так же, как с умным (правый).
Перекрытый оператор служит именно этой цели: одинаковости внешнего вида.

RL>Мне понятна запись:

RL>
RL>С operator + (const C lPar, const C rPar);
RL>


RL>Читаю (в соответствии с тем как Вы меня учили)

RL>Внешний оператор;
RL>двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);

RL>Возможны две формы вызова

RL>явная:
RL>C x,y,z;

RL>z = +(x,y);

RL>и операторная:
RL>С x,y,z;

RL>z = x+y;

RL>Верно?

Неверно!!!
Операторная запись:
 z = x+y;

Функциональная запись (если оператор внешний):
 z = operator+ (x,y);

Функциональная запись (если оператор — функция-член):
 z = x.operator+ (y);


RL>Теперь оператор-член класса:

RL>
RL> class С
RL>{
RL>  C add(val1, val2);
RL>  C operator + (C rPar) const {return add(*this, rPar);}
RL>};
RL>

RL>Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)

Он не "если не указан", а всегда не указан. У любого метода объекта есть неявный параметр — указатель на сам объект.

RL>двухместный (бинарный) и вроде бы инфиксный;


RL>Явная форма вызова:

RL>С obj;
RL>C anyPar;
RL>C res;
RL>res = obj.operator + (anyPar);
//равносильно obj+anyPar? Т.е текущий объект складывается с параметром, и что будет блин не догоняю ((

RL>Операторная форма:

RL>С obj;
RL>C anyPar;
RL>C res;

RL>res = +anyPar;

// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????

Ещё раз. Операторная форма записи НИЧЕМ не отличается от операций над арифметическими типами.
Ключевое слово "operator" — это НЕ ОПЕРАТОРНАЯ ФОРМА ЗАПИСИ. А функциональная.

RL>Мне бы хотелось в такой же форме разобрать и эти операторы:

RL>
RL> class cPtr
RL>{
RL>   private:
RL>   T* p;//указатель на искомый интерфейс
RL>   public:
RL>   operator T*() {  return m_pI; }
RL>   T& operator*() { return *m_pI; }
RL>   T** operator&() { return &m_pI; }
RL>   T* operator->() { return m_pI; }
RL>};

RL>

RL>Прошу извинить если надоедаю

Ну и что здесь непонятного? Попробуй поработать за компилятора.
void foo(T* pointer);
void bar(T value);
void xyz(T& reference);
void get(T** address_of_pointer);

main()
{
  cPtr ptr;

  get(&ptr);              get(&(ptr.m_pI));
  foo(ptr);               foo(ptr.m_pI);
  bar(*ptr);              bar(*(ptr.m_pI));
  xyz(*ptr);              xyz(*(ptr.m_pI));
  ptr->call();            ptr.m_pI->call();
}
Перекуём баги на фичи!
Re[21]: Перегрузка операторов
От: Red Line  
Дата: 25.05.04 16:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Red Line, Вы писали:


К>
RL>>// Операции с указателем
RL>>>>T& operator*() { assert(m_pI != NULL); return *m_pI; }
К>

RL>>Я ни как не вразумею, — как нужно читать синтаксис этого оператора.

К>Проще некуда:

К>
К>struct Data
К>{
К>  int x;
К>  void foo();
К>};

К>// некоторые функции...
К>void accept_data(Data d); // входной параметр
К>void fill_data(Data& d); // выходной параметр


К>struct SmartPtr
К>{
К>  Data* p;

К>  Data& operator* () { return *p; }
К>};

К>main()
К>{
К>  Data* p1;
К>  SmartPtr p2;
К>  . . . . .

К>  // обычный указатель      // явный доступ к члену умного     // то же самое,
К>                            // -- обычному указателю           // но завёрнутое в оператор разыменования
К>  fill_data(*p1);           fill_data(*(p2.p));                fill_data(*p2);
К>  accept_data(*p1);         accept_data(*(p2.p));              accept_data(*p2);
К>}
К>

К>Видишь, что внешне работа с обычным указателем (левый столбец) выглядит так же, как с умным (правый).
К>Перекрытый оператор служит именно этой цели: одинаковости внешнего вида.

RL>>Мне понятна запись:

RL>>
RL>>С operator + (const C lPar, const C rPar);
RL>>


RL>>Читаю (в соответствии с тем как Вы меня учили)

RL>>Внешний оператор;
RL>>двухместный инфиксный оператор (операнд слева — lPar, операнд справа — rPar);

RL>>Возможны две формы вызова

RL>>явная:
К>
RL>>C x,y,z;

RL>>z = +(x,y);
К>

RL>>и операторная:
К>
RL>>С x,y,z;

RL>>z = x+y;
К>

RL>>Верно?

К>Неверно!!!

К>Операторная запись:
К>
 z = x+y;

К>Функциональная запись (если оператор внешний):
К>
 z = operator+ (x,y);

К>Функциональная запись (если оператор — функция-член):
К>
 z = x.operator+ (y);


RL>>Теперь оператор-член класса:

RL>>
RL>> class С
RL>>{
RL>>  C add(val1, val2);
RL>>  C operator + (C rPar) const {return add(*this, rPar);}
RL>>};
RL>>

RL>>Оператор-член класса; (подразумевается, что если левый аргумент не указан то это всегда *this)

К>Он не "если не указан", а всегда не указан. У любого метода объекта есть неявный параметр — указатель на сам объект.


RL>>двухместный (бинарный) и вроде бы инфиксный;


RL>>Явная форма вызова:

К>
RL>>С obj;
RL>>C anyPar;
RL>>C res;
RL>>res = obj.operator + (anyPar);
К>
//равносильно obj+anyPar? Т.е текущий объект складывается с параметром, и что будет блин не догоняю ((


RL>>Операторная форма:

К>
RL>>С obj;
RL>>C anyPar;
RL>>C res;

RL>>res = +anyPar;
К>

К>// правый операднд (параметр) указан явно, а левый есть объект. Какой??? anyPar-объект, а тот что в операторе где????

К>Ещё раз. Операторная форма записи НИЧЕМ не отличается от операций над арифметическими типами.

К>Ключевое слово "operator" — это НЕ ОПЕРАТОРНАЯ ФОРМА ЗАПИСИ. А функциональная.

RL>>Мне бы хотелось в такой же форме разобрать и эти операторы:

RL>>
RL>> class cPtr
RL>>{
RL>>   private:
RL>>   T* p;//указатель на искомый интерфейс
RL>>   public:
RL>>   operator T*() {  return m_pI; }
RL>>   T& operator*() { return *m_pI; }
RL>>   T** operator&() { return &m_pI; }
RL>>   T* operator->() { return m_pI; }
RL>>};

RL>>

RL>>Прошу извинить если надоедаю

К>Ну и что здесь непонятного? Попробуй поработать за компилятора.

К>
К>void foo(T* pointer);
К>void bar(T value);
К>void xyz(T& reference);
К>void get(T** address_of_pointer);

К>main()
К>{
К>  cPtr ptr;

К>  get(&ptr);              get(&(ptr.m_pI));
К>  foo(ptr);               foo(ptr.m_pI);
К>  bar(*ptr);              bar(*(ptr.m_pI));
К>  xyz(*ptr);              xyz(*(ptr.m_pI));
  ptr->>call();            ptr.m_pI->call();
К>}
К>



// обычный указатель      // явный доступ к члену умного     // то же самое,
К>                            // -- обычному указателю           // но завёрнутое в оператор разыменования
К>  fill_data(*p1);           fill_data(*(p2.p));                fill_data(*p2);
К>  accept_data(*p1);         accept_data(*(p2.p));              accept_data(*p2);


Кажется я понял !!!



 fill_data(*p2);
 accept_data(*p2);


*p2 — это и есть вызов операторной функции, т.е не звёздочка сама по себе, а именно "*p2" и есть вызов
аналогичный p2.operator*() для экземпляра p2... Этот вызов приведёт к тому, что отработает код:

 return *p;


и функция accept_data получит аргумент верного тип (типа Data).

Ну так или нет?
Re[22]: Перегрузка операторов
От: Кодт Россия  
Дата: 25.05.04 16:36
Оценка:
Здравствуйте, Red Line, Вы писали:

RL>Кажется я понял !!!


RL>
RL> fill_data(*p2);
RL> accept_data(*p2);
RL>


RL>*p2 — это и есть вызов операторной функции, т.е не звёздочка сама по себе, а именно "*p2" и есть вызов

RL>аналогичный p2.operator*() для экземпляра p2... Этот вызов приведёт к тому, что отработает код:

RL>
RL> return *p;
RL>


RL> и функция accept_data получит аргумент верного тип (типа Data).


RL>Ну так или нет?


Именно так!

ЗЫ
Не оверквоть пожалуйста
Перекуём баги на фичи!
Re[10]: Перегрузка операторов
От: Павел Кузнецов  
Дата: 26.05.04 00:07
Оценка:
> ПК>Такой подход — прямая дорога к замусориванию языка. Далеко не все "фичи" хорошо уживаются вместе (яркий пример — спецификация исключений и шаблоны).
>
> А как они не уживаются? Или ты про то, что сделав catch( complex<int> ) я не поймаю catch( complex<float> ) ?

Добавлять спецификацию исключений к шаблонам функций — зачастую совершенно бесперспективное занятие. Также наличие шаблонов закрывает простые пути проверки спецификации исключений на этапе компиляции. Но в данном случае полезность по-крайней мере одной из этих двух "фич" очевидна

> ПК>Что ты подразумеваешь под "универсальным type_traits"? Какое конкретное ты видишь применение "указателям на конструкторы" и "указателям на деструкторы"?

>
> Например, (моя идея-фикс) определение PODности типов.

Дык, а причем здесь адреса конструкторов? Это более общая проблема: кроме определения является ли данный тип POD-типом, в желаемых type_traits есть еще очень много вещей, где нужна поддержка компилятора. Соответственно, вводить бесполезную саму по себе фичу только для того, чтобы решить только одну проблему в type_traits, при том, что все равно будут нужны какие-то решения для остальных моментов, — я просто не вижу смысла.

> ПК>Перечисли характерные особенности "указателей на конструкторы" и "указателей на деструкторы". В частности:

> ПК>
  • Каковы правила приведения между "указателями на конструкторы" разных типов, "указателями на деструкторы"?
    >
    > Никаких правил. Только к void * и обратно.

    Ни указатели на функции, ни указатели на члены к void* не приводятся.

    > ПК>
  • Приведи примеры использования и т.п.
    >
    > http://www.rsdn.ru/Forum/Message.aspx?mid=608043
    Автор: adontz
    Дата: 15.04.04
    оценки сообщению прошу считать признанием полезности фичи


    В общем, я понял: сами по себе указатели на конструкторы тебе не нужны, тебе нужна возможность определения является ли некоторый тип POD-типом Против последнего особых возражений нет.
    Posted via RSDN NNTP Server 1.9 alpha
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.