Re: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 22.05.04 15:45
Оценка: 23 (6) +1
Здравствуйте, Red Line, Вы писали:

Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.

Что такое оператор? Любой? Это всего на всего некоторая фукция, кстати возвращающая значение, но у этой функции есть три особенности.
  1. Имя. Имена операторов стандартизированны. Ты можешь как угодно использовать те, что есть, но не можешь создавать свои
  2. Количество параметров. Ты мошешь как угодно обрабатывать параметры, но не можешь изменить их число. Не может быть операции + на тремя аргументами.
  3. Синтаксис вызова. Если для обычной функции параметры перечисляются в скобках, через запятую, после имени функции, то для операторов это не так.

Рассмотрим простой пример
int a = 3;
int b = 5;
int c = a + b;

Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.
Это встроенные операторы. Что это значит? Это значит, что ими можно пользоватся без написания своего кода. Они готовы к употребления с самого начала.

Что такое перегрузка операторов? Это определение оператора для какого-то типа(или типов если параметров несколько), для которого такой оператор ещё не опредлён. То есть, например, нельзя определить оператор + для параметров типа int потому что такой оператор (не важно, встроенный или нет) уже определён.

Например у нас есть тип комплексных чисел. Очевидно, что запись
complex a;
complex b;
complex c = a + b;

Понятнее и проще, чем
complex a;
complex b;
complex c = ComplexAdd(a, b);

Вот для того чтобы проще писать программы операторы и перегружают.
Есть однако одно исключение, оператор присваивания =. Его надо перегружать, если копирование объекта выполняется каким-то особым образом.
Например рассмотрим класс строки.
class string
 {
  private:
   char * str;
   int len;
  public:
   string(char * init)
    {
     len = strlen(init);
     str = new char[len + 1];
     strcpy(str, init);
    }
   ~string()
    {
     delete[] str;
    }
 }

Вроде бы класс правильный. Мы выделяем память в конструкторе и освобождаем в деструкторе. Но я не написал оператор присваивания для этого класса. Что же будет? Он сгенерируется автоматически. Такой оператор присваивания будет поэлементно (не побитово!) копировать поля из одного объекта в другой.
Как же это будет работать?
Вот простой пример
{
string a("aaaa");
string b("bbb");

b = a;
}

Итак. (Адреса памяти взяты для примера, они могут быть совершенно другими)
Сперва в конструкторе a выделится 5 байт для строки "aaaa" по адресу 0x00100000 и она будет скопирована в выделенную память.
Затем в конструкторе b выделится 4 байта для строки "bbb" по адресу 0x001000005 и она будет скопирована в выделенную память.
Далее полю str объекта b будет присвоена значение поля str объекта a и оно станет равно 0x00100000, а полю len объекта b будет присвоено значение поля len объякта a и оно станет равно 5.
Затем в деструкторе a мы особождаем ранее выделенную память по адресу 0x00100000.
Далее в деструкторе b мы особождаем ранее выделенную память по адресу 0x00100000.

Что мы имеем? Мы дважда освободили память по адресу 0x00100000 и ни разу не осободили память по адресу 0x00100005.
Первое приведёт к ошибке выполнения, второе к утечке памяти.
Как же исправить такое неправильное поведение? Надо определить оператор присваивания. Свой, не тривиальный оператор, который всё сделает как надо. А вот и он
class string
 {
  private:
   char * str;
   int len;
  public:
   string(char * init)
    {
     len = strlen(init);
     str = new char[len + 1];
     strcpy(str, init);
    }
   ~string()
    {
     delete[] str;
    }

// Возвращаем ссылку на string.
// Если не знаешь, что такео сылки, не надо утруждать себя мыслями зачем,
// достаточно пока знать, что это позволяет избежать лишнего копирования
   string & operator = (const string & s) 
    {
// Удаляем старую строку, она нам больше не понадобится.
// Обрати внимание, что это очень похоже на код деструктора
     delete[] str; 
// Выясняем длину строки и выделяем необходимое количество байт. Далее копируем новую строку в выделенную память.
// Обрати внимание, что это очень похоже на код конструктора
     len = s.len;
     str = new char[len + 1];
     strcpy(str, s.str);
// Возвращаем то, гед сейчас новое значение строки, то есть сам объект.
     return *this;
    }
 }

Вот теперь всё будет работать правильно. Справедливости ради надо сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.
string a("kuku")
string b = a; // скорее всего соптимизируется в string b(a);

Поэтому для избежания лишних проблем конструктор копирования лучше тоже написать.
   string(const string & s) 
    {
     len = s.len;
     str = new char[len + 1];
     strcpy(str, s.str);
    }
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[2]: Перегрузка операторов
От: Павел Кузнецов  
Дата: 23.05.04 09:18
Оценка: 50 (6)
> Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.

Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.

> Что такое оператор? Любой? Это всего на всего некоторая фукция


Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.

> Рассмотрим простой пример

>
> int a = 3;
> int b = 5;
> int c = a + b;
>

> Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.

Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.

> сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.

>
> string a("kuku")
> string b = a; // скорее всего соптимизируется в string b(a);
>


Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[8]: Перегрузка операторов
От: Кодт Россия  
Дата: 23.05.04 20:18
Оценка: 40 (3)
Здравствуйте, Red Line, Вы писали:

А>>Здравствуйте, adontz, Вы писали:


RL>
  >>class A
A>>    {
A>>        public:
A>>            operator int()
A>>//------------------ >> А где здесь оператор? operator - ключевое слово
A>>                {                                                     int - встроенный в комплятор тип данных
A>>                    return 15;                  Выходит что () есть обозначение оператора приведения типа? 
A>>                }
A>>    };
A>>//
A>>A a;
A>>int x = a; // x будет иметь значение 15.
RL>

RL>Почему x будет иметь значение 15? Здесь же участвует operator =, как это работает?

Давай разбираться в синтаксисе.
Операторы бывают: членами класса (методами) и внешними
class C
{
public:
  result_type operator ??? (arguments) .....
};
result_type operator !!! (arguments) .....

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

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

2) одноместные префиксные (например, -x, ~x, !x). Единственный аргумент (подразумеваемый) — это сам объект.
class C
{
public:
  static C minus(const C& arg);
    static C invert(const C& arg);
    
    C operator - () const { return negate(*this); }
};
operator !(const C& arg) { return C::minus(arg); }

3) автоинкремент, автодекремент. Бывает как префиксным, так и постфиксным. ++x, x++. Чтобы отличать, у постфиксного есть фиктивный правый аргумент типа int.
class C
{
public:
    C& operator ++ (); // обычно результат пред-инкремента - это сам объект (модифицированный)
    C operator ++ (int) // а результат пост-инкремента - копия объекта до модификации
    {
        // типичная реализация пост-инкремента выглядит так (если, конечно, оператор не несёт иной смысловой нагрузки)
        C temp = *this;
        ++(*this);
        return temp;
    }
};
// внешние операторы - аналогичны
C& operator --(C& arg);
C operator --(C& arg, int) { C temp = arg; --arg; return temp; }

4) оператор индекса x[y] -- двухместный, левый аргумент это x, правый — y. Всё аналогично инфиксным.
5) оператор функции x(y,z,t.....) -- многоместный. Левый аргумент — x — это объект. Правые аргументы — y,z,t.... Причём количество их (от 0) и типы могут быть любыми.
6) оператор приведения типа (some_type)x -- одноместный, префиксный оператор. В роли some_type могут быть и простые типы (например, int), и имена типов (LPCSTR), и выражения типов (char const*).
class C
{
public:
  operator const char* () const;
    // обрати внимание: тип возвращаемого значения совпадает с именем оператора, т.е. здесь const char*
};

7) операторы присваивания, x=y, x+=y и т.п. Это инфиксные операторы. Перекрытый x=y не может быть внешним, — только членом класса.

При вызове оператора присваивания можно помыслить, будто это обычная функция.
class A;
class B;

class C
{
public:
    void operator = (const A& rhs);
    void operator = (const C& rhs);
    ...
};
class D
{
public:
    operator A () const;
    operator B () const;
    ...
};

main()
{
    C c;
    D d;
    c = d;
}

Компилятор попытается найти наилучшую (самую короткую) форму для (c=d). При этом если нет оператора, принимающего на вход D, D&, const D&, то он попробует выполнить преобразования d к типам, объявленным в сигнатурах:
* есть ли операторы приведения типа: (A)d, (const A&)d, (C)d, (const C&)d
* есть ли конструкторы A(const D&), C(const D&)
В случае более чем одного равновозможного преобразования, компилятор выдаст ошибку.
В случае, если возможных преобразований нет — будет другая ошибка.
... << RSDN@Home 1.1.2 stable >>
Перекуём баги на фичи!
Re[10]: Перегрузка операторов
От: jazzer Россия Skype: enerjazzer
Дата: 24.05.04 14:16
Оценка: +2 :)
Здравствуйте, Red Line, Вы писали:

RL>Перевидте пожалуйста ... Что значит инфиксные и префиксные, одноместные и двухместные. Смутно очень понимаю ...


инфиксный — это значит, что символ операции стоит между своими аргументами.
префиксный, соответственно — перед, постфиксный — после.

например, сложение можно было бы записать тремя способами (не С++!): +(x,y), (x,y)+, x+y (пре-, пост- и инфиксная нотация соответственно).
Например, в С++ для сложения используется инфиксная форма, для унарного минуса -х — префиксная, для оператора вызова функции х() — постфиксная, для инкремента — и префиксная ++х, и постфиксная х++.

RL> (const C& rhs) — правый аргумент, единственное слагаемое которое будет складыватся с объектом;

RL> { return add( *this, rhs ); } — тело оператора, в котором *this — и есть тот самый подразумеваемый левый аргумент;
RL> Правильно?

Правильно

RL> Одно не могу понять — что занчит const? К чему он относится (это специальное слово говорящее, что переменная перед которой оно поставлено

RL> будет доступна только для чтения — это так я понимаю эту запись) Зачем оно здесь? К чему оно относится и как?

Поскольку речь идет о функции-члене, которая будет вызвана для какого-то объекта, то этот const означает, что сам объект (*this) внути этой функции будет константным.

RL>Если оператор это функция, то эта запись должна быть аналогична следующей:


RL>C x;

RL>C y = -()x; //Как это будет работать :???: Вот если так -(x); то логически мне понятно,- проясните плиз в чём я не прав :???:

почти так.
если ты хочешь явно использовать функциональную форму вызова оператора, то должен писать так:
C y = operator-(x); // C y = -x;
C y = operator+(a,b); // C y = a+b;


Censored. П. 5. -- ПК
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[3]: Перегрузка операторов
От: Кодт Россия  
Дата: 23.05.04 10:13
Оценка: 22 (1) +1
Здравствуйте, Павел Кузнецов, Вы писали:

>> Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.


ПК>Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.


Не пугай человека
Если коротко, то перегрузка функций (в том числе операторов) позволяет компилятору автоматически "дописывать" программы на произвольной глубине; без перегрузки — ВЕСЬ объём работы по выбору подходящих функций и типов перекладывается на программиста.
Пример: функция сортировки std::sort использует перегруженные операторы <, = и адресную арифметику. Без перегрузки — либо программист должен переписать этот алгоритм для каждого своего типа данных, либо передавать адреса соответствующих функций как callback-параметры. (сишная функция ::qsort принимает только сравнение, что существенно ограничивает её применение).

>> Что такое оператор? Любой? Это всего на всего некоторая фукция


ПК>Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.


А конструкторы/деструкторы/присваивания POD-типов — тоже?

Наверное, имеет смысл говорить о функциях и псевдофункциях.
(Хотя такого термина и нет в Стандарте, но для единообразия можно ввести)...

>> Рассмотрим простой пример

>>
>> int a = 3;
>> int b = 5;
>> int c = a + b;
>>

>> Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.

ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.


Более корректный пример был бы
int c;
c = a + b;


>> сказать, что в некоторых случаях, компилятор может использовать оператор присваивания вместо конструктора копирования. Например как здесь.

>>
>> string a("kuku")
>> string b = a; // скорее всего соптимизируется в string b(a);
>>


ПК>Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.


Соответственно,
string b; // конструктор без параметров
b = a; // оператор присваивания
... << RSDN@Home 1.1.2 stable >>
Перекуём баги на фичи!
Re[6]: Перегрузка операторов
От: Павел Кузнецов  
Дата: 24.05.04 15:08
Оценка: 22 (2)
> ПК>Хотя адрес конструктора или деструктора получить нельзя.
>
> Кстати я так и не понял почему Этому есть какое-нибудь объяснение или просто так решили?

Простой пример:
struct V
{
   V(int) { }
   virtual ~V() { }
};

struct D : virtual V
{
   D(int i) : V(i) { }
};

struct M : D
{
   M(int i) : V(i), D(i) { }
};

int main()
{
   M m(10);
}

Вот как выглядит код, порожденный компилятором для создания объекта m:
; 19   :   M m(10);

    push    1
    push    10                    ; 0000000aH
    lea    ecx, DWORD PTR _m$[ebp]
    call    ??0M@@QAE@H@Z                ; M::M

Обрати внимание на выделенную строку — это специальный внутренний параметр конструктора M, нужный компилятору для того, чтобы он не инициализировал виртуальную базу несколько раз; аналогичный параметр есть и у конструктора D. Это далеко не единственный случай, когда компилятору нужно передавать в конструктор контекст из места вызова. Также компилятору может потребоваться иметь какие-то специальные внутренние значения, возвращаемые реализацией из конструктора. Примерно то же самое и с деструкторами — компилятору нужно организовывать специальное взаимодействие с управлением памятью.

Очевидно, что "указатель на конструктор" указателем на член быть не может, т.к. объекта, который нужен для вызова функции через указатель на член, в момент "вызова" конструктора еще не существует. Кроме того, семантика "указателей на конструкторы" отличалась бы от указателей на члены: например, в отличие от указателей на члены, "указатель на конструктор" нельзя было бы приводить к "указателю на конструктор унаследованного класса", т.к., в отличие от функций-членов, конструкторы не наследуются.

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

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

И это только самая вершина айсберга...

Итак, в готовом виде такой сущности как указатели на конструкторы и деструкторы в языке нет, т.к. в общем случае нет какой-то готовой функции, на которую можно было бы выдать указатель пользователю. А так как пока не был продемонстрирован сценарий, в котором они были бы полезны, то и морочиться с описанием их семантики (которую без реальных сценариев использования представить невозможно) никто не стал.
Posted via RSDN NNTP Server 1.9 alpha
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[11]: Перегрузка операторов
От: adontz Грузия http://adontz.wordpress.com/
Дата: 24.05.04 20:52
Оценка: :))
Здравствуйте, jazzer, Вы писали:

J> ...


Тише! Это я, Кодт и ПК договорились друг другу оценки ставить за простые вопросы

Censored. Нарушение п. 5. -- ПК
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[12]: Перегрузка операторов
От: jazzer Россия Skype: enerjazzer
Дата: 25.05.04 08:47
Оценка: :))
Здравствуйте, adontz, Вы писали:

Censored. Нарушение п. 5 -- ПК
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[10]: Перегрузка операторов
От: Кодт Россия  
Дата: 24.05.04 14:17
Оценка: 21 (1)
Здравствуйте, Red Line, Вы писали:

RL>Перевидте пожалуйста ... Что значит инфиксные и префиксные, одноместные и двухместные. Смутно очень понимаю ...


По количеству операндов (аргументов) операторы бывают:
  • одноместные, унарные: -x, x++, (type)x
  • двуместные, бинарные: x*y, x[y]
  • трёхместные, тернарные: x?y:z
  • многоместные: в С++ это оператор вызова функции, f(x,y,z,t....).
    По расположению операндов:
  • префиксные — перед операндом: -x, ++x, (type)x
  • инфиксные — между операндами: x+y, x?y:z
  • постфиксные — после операнда: x++
    Принято считать, что операторы со скобками — () и [] — постфиксные. Впрочем, хоть горшком назови...

    К>>class C
    К>>{
    К>>public:
    К>>    // функциональная форма
    К>>  static C add(const C& lhs, const C& rhs);-----------------------> Это функция
    К>>  static C subtract(const C& lhs, const C& rhs);
    
    К>>  C operator + (const C& rhs) const { return add( *this, rhs ); } --------------> Это оператор.


    RL>По моему мнению у оператора + должно быть два входных параметра и возвращаемое значение.

    RL>Т.е С operator + (const& lhs, const& rhs){return add(lhs, rhs);}
    RL>Ваша запись:
    RL>C operator + (const C& rhs) const { return add( *this, rhs ); }
    RL>мне не понятна.
    RL>Вы пишите :
    RL>"инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) — это сам объект, а правый указывается в сигнатуре."
    RL>Можно ли это читать так: .... м-да.... кажись понял ))) Т.е одно слагаемое здесь всегда постоянно (это сам объект), а другое передаётся через параметр. Так?
    RL>(const C& rhs) — правый аргумент, единственное слагаемое которое будет складыватся с объектом;
    RL>{ return add( *this, rhs ); } — тело оператора, в котором *this — и есть тот самый подразумеваемый левый аргумент;
    RL>Правильно?
    RL>Одно не могу понять — что занчит const? К чему он относится (это специальное слово говорящее, что переменная перед которой оно поставлено будет доступна только для чтения — это так я понимаю эту запись) Зачем оно здесь? К чему оно относится и как?

    Пожалуйста, воздержись от вставки рассуждений в текст программы. В форуме это смотрится ужасно.

    Если оператор объявляется как член класса, то самый левый его операнд — это объект данного класса, то есть *this.
    Я специально проиллюстрировал, как оператор можно реализовать через функцию.
    Если у нас есть функция add(lhs,rhs), то оператор-член выглядит как
     C  C::operator+ (const C& rhs) const { return add(*this,rhs); }

    а внешний
     C  ::operator+ (const C& lhs, const C& rhs) { return add(lhs,rhs); }


    Что значит const после списка параметров метода (будь то оператор или обычная функция)?
    Это способ сообщить, что this в контексте данного метода имеет тип const C*, то есть в этом методе объект не меняется.
    Опять же, сравни оператор-член и внешний.

    Почему оператор сложения должен быть константным?
    На самом деле, не должен. Но обычно операция сложения не меняет операнд. (x=y+z), y до и после сложения не меняется.

    Обычно операторы перекрывают, сохраняя их смысл над новыми множествами аргументов.
    То есть + это сложение (для строк — конкатенация),
    ! это отрицание (проверка на ноль, на пустоту, на бессодержательность),
    ++ это инкремент и т.д.

    Иногда перекрытому оператору даётся иной смысл, меняющий и правила (но не синтаксис) его использования.
    Например, операторы арифметического сдвига:
    unsigned int operator<< (unsigned int x, unsigned int n);
    unsigned int operator>> (unsigned int x, unsigned int n);

    (x<<n) = x*(2^n)
    (x>>n) = x/(2^n)
    где ^ — возведение в степень.
    А те же операторы, применённые к файловым потокам:
    ostream& operator<< (ostream& ostr, T value);
    istream& operator>> (istream& istr, T& value);

    означают запись в поток (модификация левого операнда!) и чтение из потока (модификация обоих операндов!)...

      
    К>>};
    К>>C operator - (const C& lhs, const C& rhs) { return C::subtract(lhs, rhs); }

    RL>А эта запись мне понятна. Тут разность вычисляется методом класса C::subtract(lhs, rhs)
    RL>и возвращается из оператора.

    К>>2) одноместные префиксные (например, -x, ~x, !x). Единственный аргумент (подразумеваемый) — это сам объект.

    К>>
    К>>class C
    К>>{
    К>>public:
    К>>  static C minus(const C& arg);
    К>>    static C invert(const C& arg);
        
    К>>    C operator - () const { return negate(*this); }
    
    
    К>>};

    RL>Правильно ли это читать так:
    RL>Если "-" будет поставлен перед переменной типа С (аргументов то никаких нет ) значит отработает вот этот код:
    RL>return negate(*this);

    RL>Т.е
    RL>C x;
    RL>C y = -x;

    Именно так.

    RL>Если оператор это функция, то эта запись должна быть аналогична следующей:

    RL>C x;
    RL>C y = -()x;

    RL>Как это будет работать Вот если так -(x); то логически мне понятно,- проясните плиз в чём я не прав
    Синтаксис операторов — перекрытых или неперекрытых — жёстко определён языком. Конструкция -()x синтаксически неверна, вне зависимости от типа аргумента.

    Всего существуют 2 формы: операторная (как обычно) и функциональная (как бы вызов функции).

    Касательно — и ! это выглядит так:
    y = x.operator-();    y = -x;
    y = operator!(x);     y = !x;


    К>>operator !(const C& arg) { return C::minus(arg); }

    — причём тут minus(arg) — здесь по идее должна быть инверсия
    Ну перепутал, бывает

    RL> Да и к тому же в заголовке этого пункта Вы говорите "Единственный аргумент (подразумеваемый) — это сам объект." Тогда это что — const C& arg, это же агрумент


    Дык это же внешний оператор. Не хотел лишний раз повторяться.
    А у оператора-члена аргумент подразумеваемый, поэтому в списке явных аргументов — пусто.

    К>>3) автоинкремент, автодекремент. Бывает как префиксным, так и постфиксным. ++x, x++. Чтобы отличать, у постфиксного есть фиктивный правый аргумент типа int.-------->>


    RL>Он всегда имеет тип int чтобы отличался от прификсного чисто визуально?


    Да, всегда. И обычно принимает значение 0.
    То есть если ты напишешь
    class C
    {
    public:
      void operator++(int t) { cout<<"postfix increment (" << t << ")"; }
      // наплевать на возвращаемое значение
    };
    
    main()
    {
      C c;
      c++;
    }

    то увидишь 0.

    RL>Впрочем если не считать того, что я не сумел понять из предыдущего пункта, то этот мне в принципе понятен.

  • Перекуём баги на фичи!
    Re[6]: Перегрузка операторов
    От: Павел Кузнецов  
    Дата: 24.05.04 05:15
    Оценка: 10 (1)
    > ПК>Конструкторы/деструкторы есть не у всех POD-типов, а только у классов. У классов конструкторы, деструкторы и оператор присваивания функциями являются в том смысле, что они окружены точками следования и т.п.
    >
    > А что это такое — точка следования? Первый раз слышу

    Например:
    http://rsdn.ru/Forum/Message.aspx?mid=94162&amp;only=1
    Автор: Павел Кузнецов
    Дата: 02.09.02

    http://rsdn.ru/Forum/Message.aspx?mid=521301&amp;only=1
    Автор: Павел Кузнецов
    Дата: 28.01.04


    > ПК>Хотя адрес конструктора или деструктора получить нельзя.

    >
    > Кстати я так и не понял почему Этому есть какое-нибудь объяснение или просто так решили?

    Об этом чуть позже, если будет время.
    Posted via RSDN NNTP Server 1.9 alpha
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[4]: Перегрузка операторов
    От: Павел Кузнецов  
    Дата: 23.05.04 22:47
    Оценка: 9 (1)
    > ПК> Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.
    >
    > А конструкторы/деструкторы/присваивания POD-типов — тоже?

    Конструкторы/деструкторы есть не у всех POD-типов, а только у классов. У классов конструкторы, деструкторы и оператор присваивания функциями являются в том смысле, что они окружены точками следования и т.п. Хотя адрес конструктора или деструктора получить нельзя.

    > Наверное, имеет смысл говорить о функциях и псевдофункциях.

    > (Хотя такого термина и нет в Стандарте, но для единообразия можно ввести)...

    Конструкторы/деструкторы стандарт называет специальными функциями.
    Posted via RSDN NNTP Server 1.9 alpha
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[4]: Перегрузка операторов
    От: jazzer Россия Skype: enerjazzer
    Дата: 24.05.04 12:08
    Оценка: +1
    Здравствуйте, adontz, Вы писали:

    ПК>>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.


    A>Для классов да, но разве для POD типов это не присваивание? Я долго читал раздел 8.5 стандарта (ИМХО на редкость непонятный) и понял именно так. :xz:


    То, что это инициализация, напрямую следует из грамматики:
    simple-declaration:
       decl-specifier-seq opt init-declarator-list opt ;
    
    init-declarator:
       declarator initializer opt
    
    initializer:
       = initializer-clause
       ( expression-list )

    Так что любая запись вида
    тип имя = что-то;

    являет собой объявление с инициализацией.

    И POD-овость типа тут вообще ни причем.
    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[12]: Перегрузка операторов
    От: Кодт Россия  
    Дата: 24.05.04 21:48
    Оценка: :)
    Здравствуйте, adontz, Вы писали:

    A>Тише! Это я, Кодт и ПК договорились друг другу оценки ставить за простые вопросы


    На! Получай свою заслуженную оценку
    ... << RSDN@Home 1.1.2 stable >>
    Перекуём баги на фичи!
    Re[10]: Перегрузка операторов
    От: Кодт Россия  
    Дата: 25.05.04 09:59
    Оценка: +1
    Здравствуйте, adontz, Вы писали:

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


    Прошу считать мою оценку признанием удивительности фичи.
    "И от кота польза: он мышей ловить будет. — А у нас нет мышей! — А мы заведём!" (Простоквашино).

    Всё-таки надо различать
  • выражение placement new, которое можно записать и как new(&obj)Class(x,y,z), и как obj.__ctor(x,y,z),
  • кусок ассемблерного кода, выполняющий конструирование
    Поскольку какая подпольная работа ведётся в этом коде — зависит от реализации. Может, там есть скрытый параметр — булев флаг, управляющий конструированием виртуальной базы, а может, ещё что-то.

    Вот аналогия:
    class A
    {
    public:
      virtual void foo();
    };
    
    class B : public A
    {
    public:
      virtual void foo()
      {
        . . .
        A::foo(); // выражение статического вызова
        . . .
      }
    
      void bar()
      {
        void (A::*method)() = &A::foo; // указатель на динамический метод
        this->*method(); // то же, что и this->foo(), т.е. this->B::foo();
      }
    };




    Я не спорю, что фича __ctor/__dtor полезна тем, что упрощает синтаксис placement new, а также (как бонус) позволяет дискриминировать POD-типы.
    Но добывать где-то адрес конструктора для дела?
  • Перекуём баги на фичи!
    Перегрузка операторов
    От: Red Line  
    Дата: 22.05.04 14:33
    Оценка:
    Объясните пожалуйста что означает перегрузка операторов.
    Поясните на простом примере.

    Заранее благодарен всем!
    ... << RSDN@Home 1.1.3 stable >>
    Re[2]: Перегрузка операторов
    От: Red Line  
    Дата: 22.05.04 16:45
    Оценка:
    Здравствуйте, adontz, Вы писали:

    A> <...>


    Большое спасибо за грамотыный и исчерпывающий ответ! Но есть всё же несколько не понятных моментов.
    1)
    string & operator = (const string & s) — означает что аргументами оператора = могут быть данные только типа string.
    Я правильно понял? Т.е это будет работать только если и в левой и в правой части выражения будут переменные типа string:
    string a;
    string b;
    a = d;//здесь произойдёт перегрузка оператора



    2) Можно ли переопределить оператор так:
    string & operator = (int value)
    Будет ли это работать?

    class A
    {
    int Res;
    public:
    //Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
    };

    Извините, что не много сумбурно...
    ... << RSDN@Home 1.1.3 stable >>

    Удалено избыточное цитирование. -- ПК
    Re[2]: Перегрузка операторов
    От: Paranoik  
    Дата: 22.05.04 16:45
    Оценка:
    Здравствуйте, adontz, Вы писали:
    A>...
    A>Рассмотрим простой пример
    A>
    A>int a = 3;
    A>int b = 5;
    A>int c = a + b;
    A>

    A>Формально здесь задействованно 2 оператора. Оператор присваивания = и оператор сложения +.
    A>...
    Это конструктор с инициализацией вроде, а не operator=.
    Дружба не наследуется и не транзитивна.
    ©Бьерн Страуструп
    Re[3]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 22.05.04 18:57
    Оценка:
    Здравствуйте, Paranoik, Вы писали:

    P>Это конструктор с инициализацией вроде, а не operator=.


    Насколько мне известно, это operator =, который может быть оптимизирован в конструктор копирования.
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[3]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 22.05.04 19:15
    Оценка:
    Здравствуйте, Red Line, Вы писали:

    RL> string & operator = (const string & s) — означает что аргументами оператора = могут быть данные только типа string.


    Да

    RL> Я правильно понял? Т.е это будет работать только если и в левой и в правой части выражения будут переменные типа string:


    Либо что-то, что можно привести к типу string. Например если ты напишешь класс string2, такого вида
    class string2: public string
     {
     }

    или такого вида
    class string2
     {
      public:
       operator string(); // оператор преобразования типа
     }

    То для таких классов этот оператор присваивания тоже будет работать.

    RL> a = d;//здесь произойдёт перегрузка оператора


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

    RL>2) Можно ли переопределить оператор так:

    RL> string & operator = (int value)

    Да, можно.

    RL> Будет ли это работать?


    А почему бы и нет?

    RL> class A

    RL> {
    RL> int Res;
    RL> public:
    RL> //Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
    RL> };

    Нет, так нельзя. Можно написать оператор который при вызове A = 5, запишет в Res пятёрку.

    ЗЫ: Стирай ненужные части сообщения, на которое отвечаешь.
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[4]: Перегрузка операторов
    От: Paranoik  
    Дата: 22.05.04 19:47
    Оценка:
    Здравствуйте, adontz, Вы писали:

    A>Насколько мне известно, это operator =, который может быть оптимизирован в конструктор копирования.


    class TTest
    {
    public:
      TTest(int){cout << "Constructor";}
      int operator=(int){cout << "Operator";return 1;}
    };
    
    int main( int argc, char * argv[] )
    {
      TTest test = 7;
      return 0;
    }

    Constructor

    Теперь изменим:
    class TTest
    {
    public:
    //  TTest(int){cout << "Constructor";}
      int operator=(int){cout << "Operator";return 1;}
    };
    
    int main( int argc, char * argv[] )
    {
      TTest test = 7;
      return 0;
    }

    "untitled1.cpp": E2034 Cannot convert 'int' to 'TTest' in function main(int,char * *) at line ...
    Дружба не наследуется и не транзитивна.
    ©Бьерн Страуструп
    Re[5]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 22.05.04 20:42
    Оценка:
    Здравствуйте, Paranoik, Вы писали:

    P>"untitled1.cpp": E2034 Cannot convert 'int' to 'TTest' in function main(int,char * *) at line ...


    Тест некорректный. Для классов (non-POD types) действуют другие правила.
    А дял не лкссов мы ничего не сможем проверить Поэтоve остаётся hfpdt xnj отослать к 8.5 стандарта зотя, если честно, мне самому это как аргумент не нравится
    Почему-то мне достался PDF со стандартом из которого нельзя делать Copy/Paste. Побежал искать лекарства
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[4]: Перегрузка операторов
    От: Red Line  
    Дата: 23.05.04 07:33
    Оценка:
    Здравствуйте, adontz, Вы писали:


    class string2
     {
      public:
       operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
     }




    RL>> class A

    RL>> {
    RL>> int Res;
    RL>> public:
    RL>> //Можно ли написать оператор, который при вызове A.Res = 5; запишет целое число в поле Res моего класа A/
    RL>> };

    A>Нет, так нельзя. Можно написать оператор который при вызове A = 5, запишет в Res пятёрку.


    Можно ли решить такую задачу:
    Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
    чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
    Я начну — если напутаю с исключениями поправьте плиз...


    clas error
    {
       int errorCode;
     public:
       error(int value) {errorCode = value;}
       
       void ShowError()
       {
         if (errorCode == 1) MessageBox(...)....
         if (errorCode == 2) MessageBox(...)....
       }
    };
    
    class A
    {
      public:
      (A) operator = (int RetCode)
      {
        if !RetCode return 0;
        throw error(RetCode);------------------------------------>> Вот это и есть тот "паразитный" код который я пытаюсь убрать из основного текста
                                                                    программы и скрыть его внутри оператора =
      } 
    };
    
    A a;   
    .....
    .....
    Гдето в коде...
    a = CreateSemaphore(...); ---------------->> вот здесь по замыслу, должен вызываться перегруженый оператор и создавать объект исключения,
                                                 который потом будет пойман в catch error& {} 
    
    catch(error& e)
    {
      e.ShowError();
    }


    У меня ничего не вышло... В чём моя ошибка?
    Re[5]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 08:53
    Оценка:
    Здравствуйте, Red Line, Вы писали:

    RL>
    RL>class string2
    RL> {
    RL>  public:
    RL>   operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
    RL> }
    RL>


    Каждый раз, когда нужно будет преобразование к типу string будет вызван оператор string().

    Вот простой пример.

    class A
        {
            public:
                operator int()
                    {
                        return 15;
                    }
        };
    //
    A a;
    int x = a; // x будет иметь значение 15.


    RL>Можно ли решить такую задачу:

    RL>Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
    RL>чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
    RL>Я начну — если напутаю с исключениями поправьте плиз...

    Так? (см. класс A)

    //
    #include <iostream>
    //
    using namespace std;
    //
    class MyException
     {
            private:
                int value;
            public:
                MyException(const int & initValue) : value(initValue)
                 {
                 }
                int GetValue()
                    {
                        return value;
                    }
     };
    //
    class A
        {
            public:
                A & operator = (const int & returnValue)
                    {
                        if (returnValue == 0) return *this;
                        throw MyException(returnValue);
                    }
        };
    //
    class B
        {
            public:
                B(const int & returnValue)
                    {
                        if (returnValue != 0)
                            {
                                throw MyException(returnValue);
                            }
                    }
        };
    //
    #define C(returnValue) if (returnValue != 0) throw MyException(returnValue);
    //
    int main( int argc, char * argv[], char * envp[] )
        {
            A a;
            try
             {
                    cout << "1" << endl;
                    a = 1;
             }
            catch(MyException & e)
                {
                    cout << e.GetValue() << " catched" << endl;
                }
            try
             {
                    cout << "2" << endl;
                    B(2);
             }
            catch(MyException & e)
                {
                    cout << e.GetValue() << " catched" << endl;
                }
            try
             {
                    cout << "3" << endl;
                    C(3);
             }
            catch(MyException & e)
                {
                    cout << e.GetValue() << " catched" << endl;
                }
            return 0;
        }
    //
    // End of file
    //

    А вообще несколько странный способ обработки ошибок Я бы запихал ту же функциональность не в оператор присваивания, а в конструктор (класс B) или макрос (макрос С)
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[6]: Перегрузка операторов
    От: Аноним  
    Дата: 23.05.04 10:03
    Оценка:
    Здравствуйте, adontz, Вы писали:

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


    RL>>
    RL>>class string2
    RL>> {
    RL>>  public:
    RL>>   operator string(); // оператор преобразования типа---------------->> Поясните пожалуйста как он работает.
    RL>> }
    RL>>


    A>Каждый раз, когда нужно будет преобразование к типу string будет вызван оператор string().


    A>Вот простой пример.


    A>
    A>class A
    A>    {
    A>        public:
    A>            operator int()
    A>                {
    A>                    return 15;
    A>                }
    A>    };
    A>//
    A>A a;
    A>int x = a; // x будет иметь значение 15.
    A>


    RL>>Можно ли решить такую задачу:

    RL>>Необхохдимо полностью скрыть "паразитный" код в котором происходит выбрасывание исключения так,
    RL>>чтобы внешне казалось, что ошибки в программе вобще не обрабатываются.
    RL>>Я начну — если напутаю с исключениями поправьте плиз...

    A>Так? (см. класс A)


    A>
    A>//
    A>#include <iostream>
    A>//
    A>using namespace std;
    A>//
    A>class MyException
    A> {
    A>        private:
    A>            int value;
    A>        public:
    A>            MyException(const int & initValue) : value(initValue)------------------------>> Здесь не понял. Я думал, что value(initValue)
    A>             {                                                                              это константный инициализатор, который надо 
    A>             }                                                                              использовать при инициализации члена класса  
    A>            int GetValue()                                                                  являющегося константой. Поясните пожалуйста 
    A>                {                                                                       этот момент. 
    A>                    return value;
    A>                }
    A> };
    A>//
    A>class A
    A>    {
    A>        public:
    A>            A & operator = (const int & returnValue)------------------------------------->> я не проверял как это работает, но мне не
    A>                {                                                                       понятно почему Вы указываете в качестве воз 
    A>                    if (returnValue == 0) return *this;                             вращаемого оператором значения A&??? 
    A>                    throw MyException(returnValue);                                 Так же не понятно почему const int&                }                                                                               а не просто const int. Для чего ссылка? 
    A>    };                                                                                              Думаю, что я не правильно понимаю, что
    A>//                                                                                                    такое ссылка. В моём понимании ссылка есть
                                                                                                            авто разименовывающийся указатель. Т.е
                                                                                                            *returnValue писать не нужно - это произойдёт
                                                                                                            само. Т.е для удобства правильно понимаю? 
    A>class B                                                                                               
    A>    {
    A>        public:
    A>            B(const int & returnValue)
    A>                {
    A>                    if (returnValue != 0)
    A>                        {
    A>                            throw MyException(returnValue);
    A>                        }
    A>                }
    A>    };
    A>//
    A>#define C(returnValue) if (returnValue != 0) throw MyException(returnValue);
    A>//
    A>int main( int argc, char * argv[], char * envp[] )
    A>    {
    A>        A a;
    A>        try
    A>         {
    A>                cout << "1" << endl;
    A>                a = 1;-------------------->> в левой части operator = стоит переменная с типом A. Тогда почему тип значения
    A>         }                                           возвращаемого оператором = A&. Для меня ЭТО важно понять!
    A>        catch(MyException & e)
    A>            {
    A>                cout << e.GetValue() << " catched" << endl;
    A>            }
    A>        try
    A>         {
    A>                cout << "2" << endl;-------------------------->> А что это даёт в смысле применения? Мне нужно, чтобы при вызове
    A>                B(2);                                            системной ф-ции была проверка на возвращаемой ей значения. Если 
    A>         }                                                               оно не ноль, то бросать исключение . при этом весь код получающий
    A>        catch(MyException & e)                                           описание ошибки ( GetLastError() и далее FormatMessage() ) было
    A>                                                                     был скрыть внутри класса. Сокрытие здесь есть, но писать
    A>                cout << e.GetValue() << " catched" << endl;      if ( (ret = CreateSemaphre(...)) != 0 ) B(ret) не так красиво как это
    A>            }                                                        Вы показали в первом случае (при перегурзке operator =). Тогда всё полу
    A>        try                                                              чается прозрачно! Иля я не прав?
    A>         {
    A>                cout << "3" << endl;
    A>                C(3);------------------------------------------>> Лучше чем в предыдущем случае,
    A>         }                                                                
    A>        catch(MyException & e)                                             С( CreateSemaphre(...) );---->  или я не так чего то понимаю?             {
    A>                cout << e.GetValue() << " catched" << endl;
    A>            }
    A>        return 0;
    A>    }
    A>//
    A>// End of file
    A>//
    A>

    A>А вообще несколько странный способ обработки ошибок Я бы запихал ту же функциональность не в оператор присваивания, а в конструктор (класс B) или макрос (макрос С)
    Re[7]: Перегрузка операторов
    От: Red Line  
    Дата: 23.05.04 10:23
    Оценка:
    А>Здравствуйте, adontz, Вы писали:


      >class A
    A>    {
    A>        public:
    A>            operator int()------------------ >> А где здесь оператор? operator - ключевое слово
    A>                {                                                     int - встроенный в комплятор тип данных
    A>                    return 15;                  Выходит что () есть обозначение оператора приведения типа? 
    A>                }
    A>    };
    A>//
    A>A a;
    A>int x = a; // x будет иметь значение 15.
    Почему x x будет иметь значение 15? Здесь же участвует operator =, как это работает?
    Re[3]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 11:38
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Учитывая правила выбора лучшего кандидата, можно сказать, что наличие перегрузки позволяет использовать некоторые техники, которые без этого недоступны. Например, выбор лучшего алгоритма в зависимости от типа итератора, как это делается в STLport. Альтернативой этому теоретически могла бы быть частичная специализация, но выбор наиболее подходящей специализации тоже определяется через правила выбора из перегруженных функций.


    ДА, пожалуй.

    ПК>Не совсем. Предопределенные операторы для встроенных типов функциями не являются. В частности, нельзя взять их адрес, они не окружены точками следования и т.п.


    Ну это скорее исключение, а не правило. Для понимания лучше рассматривать оператор как функцию. Хотя ты конечно прав.

    ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.


    Для классов да, но разве для POD типов это не присваивание? Я долго читал раздел 8.5 стандарта (ИМХО на редкость непонятный) и понял именно так.

    ПК>Нет, оптимизация здесь ни при чем. Здесь в любом случае должен быть использован конструктор копирования (8.5/14). В частности, для string конструктор по умолчанию в данном случае не требуется, что было бы нужно, если бы стандарт предписывал использовать operator=.


    Ааа да. вот здесь ты абсолютно прав. Это я поторопился
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[7]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 11:50
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    A>> MyException(const int & initValue) : value(initValue)

    А>Здесь не понял. Я думал, что value(initValue)
    А>это константный инициализатор, который надо
    А>использовать при инициализации члена класса
    А>являющегося константой. Поясните пожалуйста
    А>этот момент.

    Такой синтаксиси можно использовать не только для константных членов класса, а для любых.

    A>> A & operator = (const int & returnValue)

    А>я не проверял как это работает, но мне не
    А>понятно почему Вы указываете в качестве воз
    А>вращаемого оператором значения A&???

    Я не зря говорил что оператор надо рассматривать как функцию.
    A = 3;
    равносильно (не то же самое, равносильно чисто логически)
    A = A.operator = (3);
    Или даже так
    A = operator = (A, 3);

    А>Так же не понятно почему const int&

    А>а не просто const int. Для чего ссылка?

    Просто хорошая привычка.

    А>Думаю, что я не правильно понимаю, что

    А>такое ссылка. В моём понимании ссылка есть
    А>авто разименовывающийся указатель. Т.е
    А>*returnValue писать не нужно — это произойдёт
    А>само. Т.е для удобства правильно понимаю?

    Если речь идёт о ссылках при передачи параметров, то в принципе понимание более или менее правильное.
    Но объяснять что такое ссылка это обширнейшая тема.

    A>>a = 1;-------------------->>

    А> в левой части operator = стоит переменная с типом A.
    А> Тогда почему тип значения
    А> возвращаемого оператором = A&.
    А> Для меня ЭТО важно понять!

    Потому что если мы возвратим A, а не A &, то должно будет до кучи произойти присваивание A = A.
    В принципе оператор
    A operator = (A);
    Вообще не будет работать.
    Кажется я плохо объяснил, но с тем что выше должно быть понятнее.

    А>А что это даёт в смысле применения? Мне нужно, чтобы при вызове

    А>системной ф-ции была проверка на возвращаемой ей значения. Если
    А>оно не ноль, то бросать исключение . при этом весь код получающий
    А>описание ошибки ( GetLastError() и далее FormatMessage() ) было
    А>был скрыть внутри класса. Сокрытие здесь есть, но писать
    А>if ( (ret = CreateSemaphre(...)) != 0 ) B(ret) не так красиво как это
    А>Вы показали в первом случае (при перегурзке operator =). Тогда всё получается прозрачно! Или я не прав?

    А почему бы не написать B(CreateSemaphore(...));

    А>С( CreateSemaphre(...) ); или я не так чего то понимаю?


    Правильно понимаешь. С B точно так же.
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[8]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 12:11
    Оценка:
    Здравствуйте, Red Line, Вы писали:

    RL>
      >>class A
    A>>    {
    A>>        public:
    A>>            operator int()------------------ >> А где здесь оператор? operator - ключевое слово
    A>>                {                                                     int - встроенный в комплятор тип данных
    A>>                    return 15;                  Выходит что () есть обозначение оператора приведения типа? 
    A>>                }
    A>>    };
    A>>//
    A>>A a;
    A>>int x = a; // x будет иметь значение 15.
    A>>

    RL>Почему x x будет иметь значение 15? Здесь же участвует operator =, как это работает?

    Какой оператор =? У класса A нет оператора = Участвует int::operator=.
    int x = a;
    в данном случае равноценно
    int x = x.operator=( A.operator(int) );

    ИМХО тебе лучше написать класса с кучей перегруженных операторо каждый из которых будет при своём выхове выдавать в консоль своё имя, а потом составляя из них конструкции смотреть что выйдет.
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[2]: Перегрузка операторов
    От: Шахтер Интернет  
    Дата: 23.05.04 17:07
    Оценка:
    Здравствуйте, adontz, Вы писали:

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


    A>Надо сразу сказать, что перегрузка чего-либо в Си++ это только для удобства программиста, но никак не то, без чего нельзя обойтись в принципе.


    С трудом себе представляю реализацию класса комплексных чисел без перегрузки операторов. Вообще, если так рассуждать, то весь C++ -- для удобства программиста, а не то, без чего нельзя обойтись в принципе. Пишут же на C.
    ... << RSDN@Home 1.1.0 stable >>
    В XXI век с CCore.
    Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
    Re[4]: Перегрузка операторов
    От: Павел Кузнецов  
    Дата: 23.05.04 17:48
    Оценка:
    > ПК>Оператора присваивания здесь нет. Здесь есть инициализация. Это суть разные вещи.
    >
    > Для классов да, но разве для POD типов это не присваивание? Я долго читал раздел 8.5 стандарта (ИМХО на редкость непонятный) и понял именно так.

    И для POD-типов здесь тоже инициализация: нигде в 8.5 нет речи о замене инициализации операциями присваивания. Вообще-то в плане инициализации разделение происходит не на POD-типы и не POD-типы, а на инициализацию ссылок, инициализацию массивов символов, инициализацию прочих массивов, инициализацию объектов классов, инициализацию объектами классов и прочую инициализацию. POD-класс типы попадают в пункты инициализации объектов классов и инициализацию объектами классов. Фундамендтальные типы, тоже являющиеся POD-типами, попадают в "прочую инициализацию". Вот пункт, применимый к фундаментальным типам:

  • Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. Standard conversions (clause 4) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered. If the conversion cannot be done, the initialization is ill-formed. [Note: an expression of type “cv1 T” can initialize an object of type “cv2 T” independently of the cv-qualifiers cv1 and cv2.
    int a;
    const int b = a;
    int c = b;

    —end note]

  • Posted via RSDN NNTP Server 1.9 alpha
    Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[3]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 19:12
    Оценка:
    Здравствуйте, Шахтер, Вы писали:

    Ш>С трудом себе представляю реализацию класса комплексных чисел без перегрузки операторов. Вообще, если так рассуждать, то весь C++ -- для удобства программиста, а не то, без чего нельзя обойтись в принципе. Пишут же на C.


    И на ассемблере
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[5]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 23.05.04 23:26
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Конструкторы/деструкторы есть не у всех POD-типов, а только у классов. У классов конструкторы, деструкторы и оператор присваивания функциями являются в том смысле, что они окружены точками следования и т.п.


    А что это такое — точка следования? Первый раз слышу

    ПК>Хотя адрес конструктора или деструктора получить нельзя.


    Кстати я так и не понял почему Этому есть какое-нибудь объяснение или просто так решили?
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[9]: Перегрузка операторов
    От: Red Line  
    Дата: 24.05.04 13:22
    Оценка:
    Здравствуйте, Кодт, Вы писали:

    К>Давай разбираться в синтаксисе.

    Давай...

    К>Операторы бывают: членами класса (методами) и внешними — понял.

    К>
    К>class C
    К>{
    К>public:
    К>  result_type operator ??? (arguments) ..... - этот оператор член класса.
    К>};
    К>result_type operator !!! (arguments) ..... - а это внешний оператор.
    К>

    К>Посмотрим, как выглядят операторы-члены.
    К>1) инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) — это сам объект, а правый указывается в сигнатуре. Сравни:

    Перевидте пожалуйста ... Что значит инфиксные и префиксные, одноместные и двухместные. Смутно очень понимаю ...

    К>
    К>class C
    К>{
    К>public:
    К>    // функциональная форма
    К>  static C add(const C& lhs, const C& rhs);-----------------------> Это функция
    К>  static C subtract(const C& lhs, const C& rhs);
    
    К>  C operator + (const C& rhs) const { return add( *this, rhs ); } --------------> Это оператор.


    По моему мнению у оператора + должно быть два входных параметра и возвращаемое значение.
    Т.е С operator + (const& lhs, const& rhs){return add(lhs, rhs);}
    Ваша запись:
    C operator + (const C& rhs) const { return add( *this, rhs ); }
    мне не понятна.
    Вы пишите : "инфиксные двухместные (например, x+y). При этом левый аргумент (подразумеваемый) — это сам объект, а правый указывается в сигнатуре."
    Можно ли это читать так: .... м-да.... кажись понял ))) Т.е одно слагаемое здесь всегда постоянно (это сам объект), а другое передаётся через параметр. Так?
    (const C& rhs) — правый аргумент, единственное слагаемое которое будет складыватся с объектом;
    { return add( *this, rhs ); } — тело оператора, в котором *this — и есть тот самый подразумеваемый левый аргумент;
    Правильно?
    Одно не могу понять — что занчит const? К чему он относится (это специальное слово говорящее, что переменная перед которой оно поставлено будет доступна только для чтения — это так я понимаю эту запись) Зачем оно здесь? К чему оно относится и как?

    К>C operator — (const C& lhs, const C& rhs) { return C::subtract(lhs, rhs); }


    А эта запись мне понятна. Тут разность вычисляется методом класса C::subtract(lhs, rhs)
    и возвращается из оператора.

    К>2) одноместные префиксные (например, -x, ~x, !x). Единственный аргумент (подразумеваемый) — это сам объект.

    К>
    К>class C
    К>{
    К>public:
    К>  static C minus(const C& arg);
    К>    static C invert(const C& arg);
        
    К>    C operator - () const { return negate(*this); }


    Правильно ли это читать так:
    Если "-" будет поставлен перед переменной типа С (аргументов то никаких нет ) значит отработает вот этот код:

    return negate(*this);

    Т.е

    C x;
    C y = -x;

    Если оператор это функция, то эта запись должна быть аналогична следующей:

    C x;
    C y = -()x; //Как это будет работать Вот если так -(x); то логически мне понятно,- проясните плиз в чём я не прав


    К>operator !(const C& arg) { return C::minus(arg); }-


    причём тут minus(arg) — здесь по идее должна быть инверсия Да и к тому же в заголовке
    этого пункта Вы говорите "Единственный аргумент (подразумеваемый) — это сам объект." Тогда это что — const C& arg, это же агрумент

    Здесь мне кажется какая-то путанится.... Поясни (перепиши) елси я прав...

    К>3) автоинкремент, автодекремент. Бывает как префиксным, так и постфиксным. ++x, x++. Чтобы отличать, у постфиксного есть фиктивный правый аргумент типа int.-------->>


    Он всегда имеет тип int чтобы отличался от прификсного чисто визуально?
    Впрочем если не считать того, что я не сумел понять из предыдущего пункта, то этот мне в принципе понятен.

    Исправлено форматирование, удалено избыточное цитирование. -- ПК
    Re[7]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 24.05.04 20:46
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Таким образом, либо нужно вводить специальный синтаксис для объявления "указателей на конструкторы",


    Спаси и созрани нас от этого

    ПК>либо компилятор должен генерировать специальные переходники, являющиеся обычными функциями.


    Это УЖЕ есть! Посмотри например код функции _initterm в VC. Она вызвает функции-заглушки вызывающие конструкторы глобальных объектов.
    В принципе это было бы абсолютно аналогично placement new, за тем простым исключением, что был бы адрес. Адрес равный NULL если конструктора нет и чему-то если есть. В конце концов адрес автоматически генерирующегося оператора присваивания получить можно, так что опять таки не проблема.

    Но главное! Деструктор-то вызывается в контексте уже созданного объекта! И виртуальными функциями всё ОК. Если я ещё соглашусь, что для получения адреса конструктора нужны дополнительные усилия, то деструктор ИМХО ничем от простого метода без параметров не отличается. Или я не прав?

    ПК>Также, для того, чтобы обеспечить саму возможность получения "указателя на конструктор", нужно будет вводить какие-то специальные правила, т.к. в текущей спецификации языка, у конструкторов нет имен, чтобы они не перекрывали имя класса в его определении.


    Получать можно точно так же как получают адрес перегруженной функции или я чего-то не вижу. Можно использовать имена __ctor, __cctor, __dtor как уже делают в Managed сообществе Было бы желание...

    ПК>И это только самая вершина айсберга...


    Это не айсберг а льдинка какая-то

    ПК>Итак, в готовом виде такой сущности как указатели на конструкторы и деструкторы в языке нет, т.к. в общем случае нет какой-то готовой функции, на которую можно было бы выдать указатель пользователю.


    ОК, указателя на контруктор нет, согласен, хотя обёрки и так генерируются и их можно было бы использовать, ну да ладно. А вот деструктор чем отличается от обычных методов?

    GR>А так как пока не был продемонстрирован сценарий, в котором они были бы полезны, то и морочиться с описанием их семантики (которую без реальных сценариев использования представить невозможно) никто не стал.


    Как это нет сценариев? А универсальный type_traits? Да и не надо так говорить. Люди вон что с шаблонами делают, а выдумывали их совсем для дуругого. Так что была бы фича, а применение найдётся
    A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[8]: Перегрузка операторов
    От: Павел Кузнецов  
    Дата: 25.05.04 01:10
    Оценка:
    > А вот деструктор чем отличается от обычных методов?

    Тем, что в него тоже может передаваться контекст. Скажем, Visual C++ выбирает несколько другой вариант: создание набора деструкторов (scalar destructor, vector destructor...). Другая реализация вместо этого может решить передавать дополнительные аргументы. С указателями на деструкторы, вообще разговор отдельный: какой, вообще, смысл в "указателе на деструктор", если он типизирован? Деструктор у класса может быть только один, соответственно, имея указатель на класс, в "указазателях на деструктор" нужды уже никакой нет.

    > Как это нет сценариев? А универсальный type_traits? Да и не надо так говорить. Люди вон что с шаблонами делают, а выдумывали их совсем для дуругого. Так что была бы фича, а применение найдётся


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

    Что ты подразумеваешь под "универсальным type_traits"? Какое конкретное ты видишь применение "указателям на конструкторы" и "указателям на деструкторы"? Перечисли характерные особенности "указателей на конструкторы" и "указателей на деструкторы". В частности:
  • Каковы правила приведения между "указателями на конструкторы" разных типов, "указателями на деструкторы"?
  • Какова сигнатура этих "функций", в частности, возвращаемое значение.
  • Приведи примеры использования и т.п.
    Posted via RSDN NNTP Server 1.9 alpha
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[9]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 25.05.04 08:17
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Тем, что в него тоже может передаваться контекст. Скажем, Visual C++ выбирает несколько другой вариант: создание набора деструкторов (scalar destructor, vector destructor...).


    Э нет. Ты меня не путай 'eh vector destructor interator' Всего лишь в цикле вызывает обычные деструкторы для каждого элемента массива. Я ещё и сам подумаю, но если я согласен про дополнительные параметры конструктора, но с деструктором у меня всё ещё сомнения. К тому же не забываем про обёртки.

    ПК>Такой подход — прямая дорога к замусориванию языка. Далеко не все "фичи" хорошо уживаются вместе (яркий пример — спецификация исключений и шаблоны).


    А как они не уживаются? Или ты про то, что сделав catch( complex<int> ) я не поймаю catch( complex<float> ) ?

    ПК>Соответственно, добавляя не слишком нужные "фичи", ты закрываешь дорогу другим, более нужным, которые ты можешь захотеть добавить позднее.


    Бывает.

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


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

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

    ПК>
  • Каковы правила приведения между "указателями на конструкторы" разных типов, "указателями на деструкторы"?

    Никаких правил. Только к void * и обратно.

    ПК>
  • Какова сигнатура этих "функций", в частности, возвращаемое значение.

    У конструкторов ссылка на объект класса, у деструкторов void.

    ПК>
  • Приведи примеры использования и т.п.

    http://www.rsdn.ru/Forum/Message.aspx?mid=608043
    Автор: adontz
    Дата: 15.04.04
    оценки сообщению прошу считать признанием полезности фичи
  • A journey of a thousand miles must begin with a single step © Lau Tsu
    Re[13]: Перегрузка операторов
    От: Red Line  
    Дата: 25.05.04 09:12
    Оценка:
    Здравствуйте, jazzer, Вы писали:

    > . . .


    Ну что успокоились? Думал написать, а Вас всё несёт и несёт...
    Вобщето переходить на личности (пусть даже через ник) есть плохой тон на форуме (впрочем как на верное и в миру).
    Думаю, господа праффессионалы, что Ваш стёб не делает Вам достоинств...

    Red Line.

    Censored. -- ПК
    Re[14]: Перегрузка операторов
    От: jazzer Россия Skype: enerjazzer
    Дата: 25.05.04 09:14
    Оценка:
    Здравствуйте, Red Line, Вы писали:

    RL>Вобщето переходить на личности (пусть даже через ник) есть плохой тон на форуме (впрочем как на верное и в миру).

    RL>Думаю, господа праффессионалы, что Ваш стёб не делает Вам достоинств...

    Действительно, господа, что-то нас занесло :(

    Red Line, приношу свои извинения

    Censored. -- ПК
    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[14]: Перегрузка операторов
    От: adontz Грузия http://adontz.wordpress.com/
    Дата: 25.05.04 09:32
    Оценка:
    Здравствуйте, Red Line, Вы писали:

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

    A> . . .


    По поводу прочитанных книг:
    Да действительно те книги которые прочитал буквально вскользь затрагивали тему этой дискуссии.
    Сейчас по почте закаказал книгу Страуструпа, буду грызть его
    Весь смысл задаваемых мной вопросов сводится к пониманию следующего кода:
    (из книги Дейла Роджерсона "Основы технологии COM").
    Я одолел 8 глав, на девятой застрял потому-что не смог понять материал по теме "перегрузка операторов"
    Тогда и решил на Ваш форум обратиться... Впрчем вот эта глава...


    Реализация класса указателя на интерфейс

    Хотя классов smart-указателей для работы с интерфейсами СОМ и не так много, как классов строк, но число тех и
    других не сильно различается. ActiveX Template Library (ATL) содержит классы указателей на интерфейсы СОМ
    CComPtr и CComQIPtr. В библиотеке MFC имеется класс CIP для внутреннего пользования. (Он находится в
    файле AFXCOM_.H.) CIP — это самый полный вариант класса smart-указателя на интерфейс. Он делает
    практически все. Здесь я представлю свой собственный, главным образом потому, что мой код легче читается.
    Мой класс похож на классы из ATL и MFC, но не столь полон.
    ----------------------------------------------
    * Точнее, для указателя CFoo*. — Прим. перев.
    ** Функция operator-> означает не «разыменуй меня», а «используй меня для обращения к моим методам или переменным-членам» — Прим..
    перев.
    ----------------------------------------------
    Клиент вызывает члены интерфейса напрямую с помощью operator->
    Доступ к функциям-членам класса smart-указателя осуществляется с использованием нотации "точка"
    Мой класс указателя на интерфейс называется IPtr и реализован в файле PTR.H, который представлен в листинге
    Пусть длина исходного текста Вас не пугает. Кода там очень мало. Я просто вставил побольше пустых строк,
    чтобы легче было читать.


    Шаблон IPtr из PTR.H
    //
    // IPtr – Smart-указатель на интерфейс
    // Использование: IPtr<IX, &IID_IX> spIX;
    // Не используйте с IUnknown; IPtr<IUnknown, &IID_IUnknown>
    // не будет компилироваться. Вместо этого используйте IUnknownPtr.
    //
    template <class T, const IID* piid> class IPtr
    {
    public:
    // Конструкторы
    IPtr()
    {
    m_pI = NULL;
    }
    IPtr(T* lp)
    {
    m_pI = lp;
    if (m_pI != NULL)
    {
    m_pI->AddRef();
    }
    }
    IPtr(IUnknown* pI)
    {
    m_pI = NULL;
    if (pI != NULL)
    {
    pI->QueryInterface(*piid, (void **)&m_pI);
    }
    }
    // Деструктор
    ~IPtr()
    {
    Release();
    }
    // Сброс в NULL
    void Release()
    140
    {
    if (m_pI != NULL)
    {
    T* pOld = m_pI;
    m_pI = NULL;
    pOld->Release();
    }
    }
    // Преобразование
    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; }
    // Присваивание того же интерфейса
    T* operator=(T* pI)
    {
    if (m_pI != pI)
    {
    IUnknown* pOld = m_pI; // Сохранить старое значение
    m_pI = pI; // Присвоить новое значение
    if (m_pI != NULL)
    {
    
    m_pI->AddRef();
    }
    if (pOld != NULL)
    {
    pOld->Release(); // Освободить старый интерфейс
    }
    }
    return m_pI;
    }
    // Присваивание другого интерфейса
    T* operator=(IUnknown* pI)
    {
    IUnknown* pOld = m_pI; // Сохранить текущее значение
    m_pI == NULL ;
    // Запросить соответствующий интерфейс
    if (pI != NULL)
    {
    HRESULT hr = pI->QueryInterface(*piid, (void**)&m_pI);
    assert(SUCCEEDED(hr) && (m_pI != NULL));
    }
    if (pOld != NULL)
    {
    pOld->Release(); // Освободить старый указатель
    }
    return m_pI;
    }
    // Логические функции
    BOOL operator!() { return (m_pI == NULL) ? TRUE : FALSE; }
    // Требует компилятора, поддерживающего BOOL
    operator BOOL() const
    {
    return (m_pI != NULL) ? TRUE : FALSE;
    }
    // GUID
    const IID& iid() { return *piid; }
    private:
    // Хранимый указатель
    141
    T* m_pI;
    };


    Разборки вырезаны. -- ПК
    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...
    Пока на собственное сообщение не было ответов, его можно удалить.