Зачем проверять на неравенство this в операторе присваивания?
От: Artifact  
Дата: 18.04.14 16:18
Оценка:
Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо
MyClass& operator=(const MyClass& other) {
    if(this != &other) {
         * * *
    }
}

писать
MyClass& operator=(const MyClass& other) {
    assert(this != &other)
    * * *
}
__________________________________
Не ври себе.
Re: Зачем проверять на неравенство this в операторе присваивания?
От: The Passenger Голландия  
Дата: 18.04.14 16:26
Оценка: 2 (2) +2
Здравствуйте, Artifact, Вы писали:

по хорошему программист должен быть параноиком и предполагать использование своего объекта во все щели
или закрывать щели ... а то потом будут тут смеятся над разработанной тобой библиотекой что она падает при приравнивании
объекта самому себе.

... и да — ассерт работает только в дебаге
Весь мир — Кремль, а люди в нем — агенты
Re: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 18.04.14 17:14
Оценка:
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе?



X a;
// много индусского кода...
const X& b = a;
// moarrrr!
a = b;



A>И, если уж так вышло, не является ли это ошибкой?

Разумеется, и по закону Мерфи выстрелит она именно в продакшене


X& operator=(const X& rhs){
    clear_me();
    X tmp(rhs);
    swap(&this, tmp); //BOOM!
    return *this;
}


Так что лучше уж проверить
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 18.04.14 21:03
Оценка: +1 -2
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо

A>
MyClass& operator=(const MyClass& other) {
A>    if(this != &other) {
A>         * * *
A>    }
A>}

A>писать
A>
MyClass& operator=(const MyClass& other) {
A>    assert(this != &other)
A>    * * *
A>}


Это всего лишь традиция, причём не особо удачная. Я бы предпочёл второй вариант с assert'ом, то есть рассматривать самоприсваивание как ошибку, нарушение precondition. Да, assert работает не всегда — но precondition и не нужно всегда проверять.
Не особо разумно вставлять дорогую проверку, которая выполняется всегда, ради каких-то маргинальных случаев.

Вот, например, что говорит Александр Степанов по этому поводу: Efficient Programming with Components: Lecture 2 Part 2
Re: Зачем проверять на неравенство this в операторе присваивания?
От: __kot2  
Дата: 18.04.14 21:28
Оценка:
Здравствуйте, Artifact, Вы писали:
A>Разве кто-то в здравом уме станет присваивать что-то самому себе?
да конечно никто никогда такой фигней не мается
чаще бывает, что кто-то пишет цикл, где делает, например swap пар обьектов и все вроде бы хорошо работает, пока случайно по какой-то причине обьекты не свопадут и этот свап не упадет из-за такого ньюанса оператора присваивания. неудобно
Re[2]: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 18.04.14 21:34
Оценка:
Здравствуйте, __kot2, Вы писали:

__>да конечно никто никогда такой фигней не мается

__>чаще бывает, что кто-то пишет цикл, где делает, например swap пар обьектов и все вроде бы хорошо работает, пока случайно по какой-то причине обьекты не свопадут и этот свап не упадет из-за такого ньюанса оператора присваивания. неудобно

Опять же — это всего лишь convention: кто будет делать проверку — сам объект или хитровывернутый алгоритм.
Так как такая проверка требуется крайне редко, то я бы предпочёл второй вариант (+assert'ы если самоприсваивание может порушить инварианты).
Re[3]: Зачем проверять на неравенство this в операторе присваивания?
От: __kot2  
Дата: 18.04.14 23:05
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Опять же — это всего лишь convention: кто будет делать проверку — сам объект или хитровывернутый алгоритм.
EP>Так как такая проверка требуется крайне редко, то я бы предпочёл второй вариант (+assert'ы если самоприсваивание может порушить инварианты).
вообще, я в своем коде таких проверок не пишу, но, думаю, для библиотечного это уже стало стандартом и надо ему следовать
Re[4]: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 18.04.14 23:12
Оценка:
Здравствуйте, __kot2, Вы писали:

__>вообще, я в своем коде таких проверок не пишу, но, думаю, для библиотечного это уже стало стандартом и надо ему следовать


Согласен — эта convention крайне популярна. Видимо этот поезд уже ушёл, к сожалению.
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Юрий Жмеренецкий ICQ 380412032
Дата: 19.04.14 03:00
Оценка: 1 (1) +1
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо

A>
MyClass& operator=(const MyClass& other) {
A>    if(this != &other) {
A>         * * *
A>    }
A>}

A>писать
A>
MyClass& operator=(const MyClass& other) {
A>    assert(this != &other)
A>    * * *
A>}


В подавляющем большинстве случаев опрератор присваивания можно реализовать с помощью метода copy-and-swap, который корректно работает без лишней проверки в случае присваивания "самому себе".
Re[2]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 21.04.14 18:23
Оценка: +2
За такое надо ... даже не могу придумать что сделать. Ну, Саттера заставить перечитать 20 раз, что ли.

B>
B>X& operator=(const X& rhs){
B>    clear_me(); // Эвона как. А если сконструировать не получится???
B>    X tmp(rhs); 
B>    swap(&this, tmp); //BOOM! Причем на этапе компиляции. А если поправить ошибку - то получим горку бессмысленного кода.
B>                      // swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен - то не получим ошибку, за исключением выше.
B>    return *this;
B>}

B>


Просто посмотреть гвайд:

X & operator = (X rhs)
{
    using namespace std;
    swap(*this, rhs);
    return *this;
}



B>Так что лучше уж проверить


Лучше не писать овнокод.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 22.04.14 13:29
Оценка: -1
Здравствуйте, Andrew S, Вы писали:


AS>Просто посмотреть гвайд:


AS>
AS>X & operator = (X rhs)
AS>{
AS>    using namespace std; // а вот за такое... да два-три раза...
AS>    swap(*this, rhs); // а оно будет работать в до-C++11 режиме, без move semantic? Чё-т неверится. Походу, обратится к operator=, не?
AS>    return *this;
AS>}
AS>
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 22.04.14 13:46
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

AS>>
AS>>X & operator = (X rhs)
AS>>{
AS>>    using namespace std; // а вот за такое... да два-три раза...
AS>>

ADL

AS>> swap(*this, rhs); // а оно будет работать в до-C++11 режиме, без move semantic? Чё-т неверится. Походу, обратится к operator=, не?

AS>>[/ccode]

AS>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.

Re[5]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 22.04.14 13:54
Оценка: -1 :)
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>

AS>>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.



Теперь осталось только уточнить, а что же будет делать перегруженный swap? Насколько я могу понять, то же самое, что раньше делал копирующий operator= И какой смысл менять шило на мыло?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[6]: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 22.04.14 14:00
Оценка: :)
Здравствуйте, slava_phirsov, Вы писали:

_>Теперь осталось только уточнить, а что же будет делать перегруженный swap? Насколько я могу понять, то же самое, что раньше делал копирующий operator= И какой смысл менять шило на мыло?


Как думаешь, зачем std::vector'у свой swap и что он делает? Или ты спрашиваешь про идиому copy-and-swap в целом?
Re[3]: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 22.04.14 14:05
Оценка: +2
Здравствуйте, Andrew S, Вы писали:

AS>За такое надо ... даже не могу придумать что сделать. Ну, Саттера заставить перечитать 20 раз, что ли.


"Те не понял, защитник братьев меньших" (с) Jay and Silent Bob
Я не призываю так писать.
Вопрос ТС был в том, кто в здравом уме будет присваивать себе.

Я привел вполне жизненный юзкейс, а также пример, когда он приведет к очень-очень плохим последствиям

B>>
B>>X& operator=(const X& rhs){
B>>    clear_me(); // Эвона как. А если сконструировать не получится???
B>>


Спасибо, Капитан!

AS>Лучше не писать овнокод.

Это все хорошо, пока живешь в мире розовых пони, которые кушают радугу и какают бабочками.
Если возраст codebase около 20 лет, а размер — несколько сотен мегабайт, случай, когда исправляешь memleak, а на нем строится некая логика в соседней либе — это не страшный сон, а реальная жизнь
Re[7]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 22.04.14 14:13
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Как думаешь, зачем std::vector'у свой swap и что он делает? Или ты спрашиваешь про идиому copy-and-swap в целом?


Я думаю, что сразу же бежать за swap для перегрузки operator= — это перебор. Хотя, если цель разработчика в том, чтобы показать свою нереальную крутизну — то да, сгодится и swap.

Цитирую оригинал

Просто посмотреть гвайд:

X & operator = (X rhs)
{
using namespace std;
swap(*this, rhs);
return *this;
}


Ну, где здесь swap перегружен? В итоге получим то, что получим.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 22.04.14 14:19
Оценка: :)
Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, Andrew S, Вы писали:



AS>>Просто посмотреть гвайд:


AS>>
AS>>X & operator = (X rhs)
AS>>{
AS>>    using namespace std; // а вот за такое... да два-три раза...
AS>>    swap(*this, rhs); // Без перегрузки перемещающего к-тора (или swap) оно, по ходу, и в C++11 уйдет в рекурсию
AS>>    return *this;
AS>>}
AS>>
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[8]: Зачем проверять на неравенство this в операторе присваивания?
От: Evgeny.Panasyuk Россия  
Дата: 22.04.14 14:23
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

EP>>Как думаешь, зачем std::vector'у свой swap и что он делает? Или ты спрашиваешь про идиому copy-and-swap в целом?

_>Я думаю, что сразу же бежать за swap для перегрузки operator= — это перебор. Хотя, если цель разработчика в том, чтобы показать свою нереальную крутизну — то да, сгодится и swap.

Идиома copy-and-swap — самый простой способ получить exception safety, причём strong

_>Цитирую оригинал

_>

_>Просто посмотреть гвайд:

_>X & operator = (X rhs)
_>{
_> using namespace std;
_> swap(*this, rhs);
_> return *this;
_>}

_>Ну, где здесь swap перегружен? В итоге получим то, что получим.

Прочитай сообщение
Автор: Andrew S
Дата: 21.04.14
полностью:

AS>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.

Re[9]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 22.04.14 14:40
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Прочитай сообщение
Автор: Andrew S
Дата: 21.04.14
полностью:

AS>>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.



Вот-вот в комментарии автор написал, что это сделать нужно, а добавить соответствующий код (ну хоть с бла-бла-бла вместо реального тела функции) — забыл. Что как бы намекает...
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Mr.Delphist  
Дата: 23.04.14 09:10
Оценка:
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой?


Задача:
В урне находится 5 синих шаров и 5 зелёных. Опускаем руку в урну и касаемся пальцем одного из шаров. Опускаем в ту же урну вторую руку и тоже касаемся пальцем одного из шаров. Какова вероятность, что пальцы обоих рук касаются одного и того же шара? Почему эта вероятность не может быть нулевой? Ответ обоснуйте. Вопрос со звёздочкой: зависит ли эта вероятность от цвета шаров?
Re: Зачем проверять на неравенство this в операторе присваивания?
От: BulatZiganshin  
Дата: 23.04.14 14:11
Оценка: 2 (1) +3 :)
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе?


кто в здравом уме станет умножать на 1? не лучше ли рассматривать это как ошибку?
Люди, я люблю вас! Будьте бдительны!!!
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Artifact  
Дата: 23.04.14 19:38
Оценка:
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо

A>
MyClass& operator=(const MyClass& other) {
A>    if(this != &other) {
A>         * * *
A>    }
A>}

A>писать
A>
MyClass& operator=(const MyClass& other) {
A>    assert(this != &other)
A>    * * *
A>}


Нашёл пример, когда такое возможно и оправдано

using namespace std;

struct MyType
{
    int value;
    
    MyType& operator=(const MyType& r) {
        if(this == &r)
            cout << "this == &r" << endl;
        value = r.value;
        return *this;
    }
    
    friend bool operator<(const MyType& l, const MyType& r) {
        return l.value < r.value;
    }
};

int main()
{
    MyType a{1};
    MyType b{2};
    
    a = min(a, b);
    
    return 0;
}
__________________________________
Не ври себе.
Re[2]: Зачем проверять на неравенство this в операторе присваивания?
От: slava_phirsov Россия  
Дата: 24.04.14 06:54
Оценка:
Здравствуйте, Artifact, Вы писали:

A>
A>    a = min(a, b); // Собственно, этого уже достаточно. 
A>

Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 24.04.14 08:36
Оценка: +1
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо

A>
MyClass& operator=(const MyClass& other) {
A>    if(this != &other) {
A>         * * *
A>    }
A>}

A>писать
A>
MyClass& operator=(const MyClass& other) {
A>    assert(this != &other)
A>    * * *
A>}


А потом вдруг окажется, что при некотором хитром сценарии работы Большого Алгоритма прасваивание самому себе все-таки возможно. И тогда придется ставить первый вариант использования, потому что вносить изменения в Большой Алгоритм себе дороже.

Собственно говоря, первый вариант позволяет нам расслабиться. Встретится присваивание самому себе раз в год, два раза на пасху. Ну и черт с ним. Во втором варианте, чем сложнее логика операций вокруг MyClass, тем больше шансов, что ружье выстрелит.
Re: Зачем проверять на неравенство this в операторе присваивания?
От: Панда Россия  
Дата: 27.04.14 10:38
Оценка:
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо


Писать "x = x", конечно, никто в здравом уме не будет.
В вот "x = y", где x, например, член класса, а y — переданный в его метод параметр — запросто.
Сам сталкивался с ситуацией, когда они случайно ссылались на один и тот же объект, а проверку вставить забыли.
Отловить быг было довольно тяжело.
Re[4]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 28.04.14 21:26
Оценка:
Здравствуйте, bydlocoder, Вы писали:

B>Здравствуйте, Andrew S, Вы писали:


AS>>За такое надо ... даже не могу придумать что сделать. Ну, Саттера заставить перечитать 20 раз, что ли.


B>"Те не понял, защитник братьев меньших" (с) Jay and Silent Bob

B>Я не призываю так писать.
B>Вопрос ТС был в том, кто в здравом уме будет присваивать себе.

B>Я привел вполне жизненный юзкейс, а также пример, когда он приведет к очень-очень плохим последствиям


B>>>
B>>>X& operator=(const X& rhs){
B>>>    clear_me(); // Эвона как. А если сконструировать не получится???
B>>>


B>Спасибо, Капитан!


AS>>Лучше не писать овнокод.

B>Это все хорошо, пока живешь в мире розовых пони, которые кушают радугу и какают бабочками.
B>Если возраст codebase около 20 лет, а размер — несколько сотен мегабайт, случай, когда исправляешь memleak, а на нем строится некая логика в соседней либе — это не страшный сон, а реальная жизнь

Вопрос был про то, зачем сравнивать. Ваш ответ, соответственно, не в кассу (более того, приведенный код просто неработоспособен). Правильный ответ — в абсолютном большинстве случаев сравнение с this не нужно. Только в очень редких кейзах, где необходимость оптимизации доказана профилированием, можно написать что-то более умное, чем типовая реализация.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[5]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 28.04.14 21:29
Оценка: +1
AS>>>Просто посмотреть гвайд:

AS>>>
AS>>>X & operator = (X rhs)
AS>>>{
AS>>>    using namespace std; // а вот за такое... да два-три раза...
AS>>>    swap(*this, rhs); // Без перегрузки перемещающего к-тора (или swap) оно, по ходу, и в C++11 уйдет в рекурсию
AS>>>    return *this;
AS>>>}
AS>>>


А какие ко мне претензии? В том посте, на который отвечал я, swap уже был. Соответственно, либо он перегружен, либо у того, кто писал тот код, ну совсем каша. Прочитайте рекомендованную литературу, право.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: Зачем проверять на неравенство this в операторе присваивания?
От: CreatorCray  
Дата: 29.04.14 06:03
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Лучше не писать овнокод.

Безусловно.
Но есть 2 "НО":
1) Люди не идеальны и делают ошибки
2) Shit happens
Re[5]: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 01.05.14 20:06
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Вопрос был про то, зачем сравнивать.

Ответ тоже был, зачем сравнивать.

AS>Ваш ответ, соответственно, не в кассу (более того, приведенный код просто неработоспособен).

В каком месте, позвольте полюбопытствовать? Вполне работоспособен. Могу пояснить.
В операторе присвоения сначала производится очистка, потом создается объект из переданного по ссылке (который уже очищен), и производится swap.
Само собой, это поведение неправильное.
Но представьте, что неправильно оно никогда не использовалось. И тут в вашей библиотеке, которая линкуется с приведенным выше кодом, написанным Раджой и Кумаром в 1995 году, используется этот объект в качестве, например, члена класса. Мне продолжать?..

AS>Правильный ответ — в абсолютном большинстве случаев сравнение с this не нужно.

Я и не говорю про абсолютно большинство. Я говорю про абсолютное меньшинство, которое позволяет метко выстрелить себе в ногу.
Re[6]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 02.05.14 23:00
Оценка:
AS>>Вопрос был про то, зачем сравнивать.
B>Ответ тоже был, зачем сравнивать.

AS>>Ваш ответ, соответственно, не в кассу (более того, приведенный код просто неработоспособен).

B>В каком месте, позвольте полюбопытствовать? Вполне работоспособен. Могу пояснить.
B>В операторе присвоения сначала производится очистка, потом создается объект из переданного по ссылке (который уже очищен), и производится swap.
B>Само собой, это поведение неправильное.
B>Но представьте, что неправильно оно никогда не использовалось. И тут в вашей библиотеке, которая линкуется с приведенным выше кодом, написанным Раджой и Кумаром в 1995 году, используется этот объект в качестве, например, члена класса. Мне продолжать?..

Неужели не очевидно, что этот код просто неработоспособен?


X& operator=(const X& rhs){
    clear_me();
    X tmp(rhs);
    swap(&this, tmp); //BOOM!
    return *this;
}


Итак, разберем 2 возможности
1. swap перегружен на X

В этом случае swap не падает, т.е. это противоречит комментарию. Значит, swap не перегружен. В целом, вообще не понятно, зачем в этом коде нужен вызов clear_me, если есть копирование во временный объект.

2. стандартная реализация swap.

Код неработоспособен вне зависимости от наличия или отсутствия проверки на this, просто из-за потенциальной бесконечной рекурсии (приведенная к условиям примера стандартная реализация swap):

template<class _Ty> inline
    void swap(_Ty& _Left, _Ty& _Right)
    {    // exchange values stored at _Left and _Right
    _Ty _Tmp = _Left;
    _Left = _Right;
    _Right = _Tmp;
    }

(на самом деле, этот код может не падать, если есть move копирование, но это, очевидно, не наш случай ввиду наличия комментария.

Т.о., в продакшен (да и вообще в любом) коде этого примера быть не могло, необходимость проверки на this он не показывает. Зачем он?

AS>>Правильный ответ — в абсолютном большинстве случаев сравнение с this не нужно.

B>Я и не говорю про абсолютно большинство. Я говорю про абсолютное меньшинство, которое позволяет метко выстрелить себе в ногу.
Вас спрашивают не почему, а зачем. Ответа пока не было. Был невнятный и неработоспособный пример и попытки его обосновать. Садитесь, два.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[7]: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 03.05.14 05:31
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Неужели не очевидно, что этот код просто неработоспособен?

Похоже, это очевидно только вам?


AS>
AS>X& operator=(const X& rhs){
AS>    clear_me();
AS>    X tmp(rhs);
AS>    swap(&this, tmp); //BOOM!
AS>    return *this;
AS>}
 
AS>


AS>Итак, разберем 2 возможности

AS>1. swap перегружен на X
Разумеется, иначе я бы указал префикс std.
Если вам так больше нравится, пусть будет
this->swap(tmp);

В контексте примера это абсолютно не важно.

AS>В этом случае swap не падает, т.е. это противоречит комментарию. Значит, swap не перегружен. В целом, вообще не понятно, зачем в этом коде нужен вызов clear_me, если есть копирование во временный объект.

Вы кроме себя, кого-то умеете читать? Я привет пример legacy-code, к которорому мы в общем случае можем не иметь доступа, например, мы используем объект из сторонней библиотеки или реализуем вокруг него какой-нибудь фасад.
Перед присвоением объект может быть очищен? Разумеется, может. Так захотелось разработчику.
Что произойдет после присвоения неконсистентного объекта, можно только гадать. Может, свалится сразу, может, при его использовании.
Вы упорно говорите, что проверка на this не нужна по причине производительности, когда как производительность — последняя причина ее реализовывать.

AS>Вас спрашивают не почему, а зачем. Ответа пока не было. Был невнятный и неработоспособный пример и попытки его обосновать. Садитесь, два.

Я думаю, если вышеприведенные примеры вас не убедили, ваша квалификация недостаточна, чтобы меня оценивать
Re[5]: Зачем проверять на неравенство this в операторе присваивания?
От: Patalog Россия  
Дата: 03.05.14 09:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

X & operator = (X rhs)
{
    using namespace std;


EP>ADL


// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.


Не догоняю, зачем здесь using?
Почетный кавалер ордена Совка.
Re: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 03.05.14 09:51
Оценка:
Здравствуйте, Artifact, Вы писали:

A>Разве кто-то в здравом уме станет присваивать что-то самому себе? И, если уж так вышло, не является ли это ошибкой? И не лучше ли тогда вместо


Раз уж такой пустяковый вопрос вызвал настоящий le bougourte, думаю, есть смысл в более развернутом примере.

#include <iostream>

// Представьте, что доступа к этопу коду мы не имеем. Физически, организационно
// или просто люди, которые его разрабатывали или поддерживали, давно отсуствуют.
// На самом деле, это не сильно отличается от первого предположения
class LegacyObj
{
public:
    LegacyObj(int i):_i(new int(i)){}

    ~LegacyObj(){
        clear();
    }

    LegacyObj(const LegacyObj& rhs){
        clear();
        _i = new int(*rhs._i);
    }

    LegacyObj& operator=(const LegacyObj& rhs){
        
        // этот вызов явно лишний, но ничего с ним сделать не можем
        clear();

        // копируем наши "ресурсы"
        _i = new int(*rhs._i);
        
        // настоящие ковбои никогда не проверяют &rhs != this
        return *this;
    }

    void clear(){
        delete _i;
        // _i = 0; - если бы еще обнулили этот указатель, это нам бы помогло при отладке
    }

    void print_me(){
        std::cout << *_i << std::endl;
    }

private:
    // вместо int* могут быть любые ресурсы, требующие аллокации
    int* _i;
};

// Какая-то обертка вокруг старого кода

// Версия с проверкой
class WrapperWithCheck
{
public:
    
    WrapperWithCheck(int a, int b, int c):_a(a),_b(b),_c(c){}

    WrapperWithCheck(const WrapperWithCheck& rhs):_a(rhs._a),_b(rhs._b),_c(rhs._c){}

    WrapperWithCheck& operator=(const WrapperWithCheck& rhs){
        if(&rhs == this)
            return *this;

        _a = rhs._a;
        _b = rhs._b;
        _c = rhs._c;
        return *this;
    }

    void print_me(){
        _a.print_me();
        _b.print_me();
        _c.print_me();
    }

private:
    LegacyObj _a;
    LegacyObj _b;
    LegacyObj _c;
};


// Версия без проверки
class WrapperWithoutCheck
{
public:

    WrapperWithoutCheck(int a, int b, int c):_a(a),_b(b),_c(c){}

    WrapperWithoutCheck(const WrapperWithoutCheck& rhs):_a(rhs._a),_b(rhs._b),_c(rhs._c){}

    WrapperWithoutCheck& operator=(const WrapperWithoutCheck& rhs){
        _a = rhs._a;
        _b = rhs._b;
        _c = rhs._c;
        return *this;
    }

    void print_me(){
        _a.print_me();
        _b.print_me();
        _c.print_me();
    }

private:
    LegacyObj _a;
    LegacyObj _b;
    LegacyObj _c;
};

int main() {
    
    {
        WrapperWithCheck wrapper_with_check(1, 2, 3);
        WrapperWithCheck other_wrapper(5, 6, 7);
        std::cout << "Object with check" << std::endl;
        wrapper_with_check.print_me();
        WrapperWithCheck& wrapper_ref = wrapper_with_check;

        // присваивание другому объекту
        other_wrapper = wrapper_with_check;

        // присваивание себе
        wrapper_with_check = wrapper_ref;
        std::cout << "After assignment" << std::endl;
        wrapper_with_check.print_me();
    }

    {
        WrapperWithoutCheck wrapper_without_check(1, 2, 3);
        WrapperWithoutCheck other_wrapper(5, 6, 7);
        std::cout << "Object without check" << std::endl;
        wrapper_without_check.print_me();
        WrapperWithoutCheck& wrapper_ref = wrapper_without_check;

        // присваивание другому объекту
        other_wrapper = wrapper_without_check;

        // присваиваем себе
        wrapper_without_check = wrapper_ref; 
        std::cout << "After assignment" << std::endl;
        // вывод здесь не определен
        // если бы обнулили указатель в clear() - получили бы debug assert и release crash
        wrapper_without_check.print_me();
    }
    return 0;
}


VC++ 12.0

Object with check
1
2
3
After assignment
1
2
3
Object without check
1
2
3
After assignment
-842150451
-842150451
-842150451


ideone output

Так что отвечая на вопрос "нужна ли проверка на присваивание самому себе", ответ — "иногда нужна"

Когда она нужна — это другой вопрос. Я бы сказал, когда мы присваиваем нетривиальные объекты. Насколько нетривиальные — надо отталкиваться от common sense approach. Нам, в конце концов, за это платят деньги.

Зачем она нужна — конечно, в последнюю очередь из соображений производительности. Как показывает пример выше (несколько надуманный, но на то он и пример), она нужна для повышения общей надежности и стабильности, особенно в крупных системах с десятками и сотнями разработчиков и сотнями мегабайт исходников.

И пожалуйста, не стоит всерьез воспринимать фразы типа "проверка на this никогда не нужна". Они происходят из категории "исключения не нужны", "писать нужно только в vim" и "Lisp — лучший язык программирования". Они выдают в вас юношу бледного со взором горящим.

Профессионал же в состоянии понять, что на многие сложные вопросы единственный правильный ответ отсутствует.
Re[8]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.05.14 12:58
Оценка:
AS>>Неужели не очевидно, что этот код просто неработоспособен?
B>Похоже, это очевидно только вам?

Не похоже.

AS>>
AS>>X& operator=(const X& rhs){
AS>>    clear_me();
AS>>    X tmp(rhs);
AS>>    swap(&this, tmp); //BOOM!
AS>>    return *this;
AS>>}
 
AS>>


AS>>Итак, разберем 2 возможности

AS>>1. swap перегружен на X
B>Разумеется, иначе я бы указал префикс std.
B>Если вам так больше нравится, пусть будет
B>
this->>swap(tmp);
B>


Тогда swap падать не будет, это противоречит комментарию. Мимо.

B>В контексте примера это абсолютно не важно.


AS>>В этом случае swap не падает, т.е. это противоречит комментарию. Значит, swap не перегружен. В целом, вообще не понятно, зачем в этом коде нужен вызов clear_me, если есть копирование во временный объект.

B>Вы кроме себя, кого-то умеете читать? Я привет пример legacy-code, к которорому мы в общем случае можем не иметь доступа, например, мы используем объект из сторонней библиотеки или реализуем вокруг него какой-нибудь фасад.
B>Перед присвоением объект может быть очищен? Разумеется, может. Так захотелось разработчику.

Не может. Если есть clear_me, то никакого конструирования во временный объект и swap не будет. Будет вызов copy_from. Что вы и продемонстрировали своим примером в другом сообщении.

B>Что произойдет после присвоения неконсистентного объекта, можно только гадать. Может, свалится сразу, может, при его использовании.

B>Вы упорно говорите, что проверка на this не нужна по причине производительности, когда как производительность — последняя причина ее реализовывать.

Такого сказано не было. Читайте внимательнее.

AS>>Вас спрашивают не почему, а зачем. Ответа пока не было. Был невнятный и неработоспособный пример и попытки его обосновать. Садитесь, два.

B>Я думаю, если вышеприведенные примеры вас не убедили, ваша квалификация недостаточна, чтобы меня оценивать

Безусловно, моя квалификация как оценивателя овнокода крайне низка. Я его всегда оцениваю крайне низко

PS Я вот не понимаю, зачем вы изворачиваетесь. Ну написали фигню, бывает. Потом ведь сами привели более корректный пример:

    LegacyObj& operator=(const LegacyObj& rhs){
        
        // этот вызов явно лишний, но ничего с ним сделать не можем// этот вызов явно лишний, но ничего с ним сделать не можем
        clear();
        // копируем наши "ресурсы"// копируем наши "ресурсы"
        _i = new int(*rhs._i);
        
        // настоящие ковбои никогда не проверяют &rhs != this// настоящие ковбои никогда не проверяют &rhs != this
        return *this;
    }


Разница с приведенным ранее очевидна. Это код хотя бы потенциально работоспособен.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[6]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.05.14 13:03
Оценка: 1 (1)
P>
P>X & operator = (X rhs)
P>{
P>    using namespace std;
P>


EP>>ADL


P>

P>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.


P>Не догоняю, зачем здесь using?


Обычный lifehack.
Внешня перегрузка swap для своего объекта (возможно, с использованием мембера swap) пишется только в том случае, если не устраивает стандартная. А стандартная (в С++11) теперь стала не устраивать сильно реже. В результате код одинаково работоспособен в любом из кейзов: как при наличии мембера swap, внешней функции swap в неймспейсе типа, перегрузки std::swap, стандартной реализации std::swap, причем всегда автоматически выбирается наиболее специализированный вариант реализации.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[9]: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 03.05.14 14:12
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Не может. Если есть clear_me, то никакого конструирования во временный объект и swap не будет. Будет вызов copy_from. Что вы и продемонстрировали своим примером в другом сообщении.

Вы, похоже, ищете повод, чтобы придраться к примеру. Можно было еще сказать, что без функции main() не скомпилируется.
Совершенно очевидно, это пример не про swap, он был приведен не как пример кода для компиляции, а как концепция.

B>>Вы упорно говорите, что проверка на this не нужна по причине производительности, когда как производительность — последняя причина ее реализовывать.

AS>Такого сказано не было. Читайте внимательнее.

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


AS>PS Я вот не понимаю, зачем вы изворачиваетесь. Ну написали фигню, бывает. Потом ведь сами привели более корректный пример


Абсолютно верно, корректный пример был приведен для компиляции, а "фигня" была для выражения концепции. Я бы мог еще написать /* а вот здесь скопируем ресурс */, и пример бы от этого не пострадал.
Если бы вы не поняли смысл исходного примера и переспросили, я бы его пояснил, и мы бы остались довольны друг другом. Но вы предпочли скатиться в хамский и менторский тон, а у меня в таких случаях пропадает желание что-либо обсуждать.
Вы хотели продемонстировать знания, а переходом на личности продемонстрировали только недостойное поведение. Некрасиво, право же
Re[10]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 03.05.14 14:29
Оценка:
AS>>Не может. Если есть clear_me, то никакого конструирования во временный объект и swap не будет. Будет вызов copy_from. Что вы и продемонстрировали своим примером в другом сообщении.
B>Вы, похоже, ищете повод, чтобы придраться к примеру. Можно было еще сказать, что без функции main() не скомпилируется.
B>Совершенно очевидно, это пример не про swap, он был приведен не как пример кода для компиляции, а как концепция.

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

B>>>Вы упорно говорите, что проверка на this не нужна по причине производительности, когда как производительность — последняя причина ее реализовывать.

AS>>Такого сказано не было. Читайте внимательнее.
B>

B>Правильный ответ — в абсолютном большинстве случаев сравнение с this не нужно. Только в очень редких кейзах, где необходимость оптимизации доказана профилированием, можно написать что-то более умное, чем типовая реализация.


И? Где здесь говориться про то, что проверка на this противоречит производительности?

AS>>PS Я вот не понимаю, зачем вы изворачиваетесь. Ну написали фигню, бывает. Потом ведь сами привели более корректный пример


B>Абсолютно верно, корректный пример был приведен для компиляции, а "фигня" была для выражения концепции. Я бы мог еще написать /* а вот здесь скопируем ресурс */, и пример бы от этого не пострадал.

B>Если бы вы не поняли смысл исходного примера и переспросили, я бы его пояснил, и мы бы остались довольны друг другом. Но вы предпочли скатиться в хамский и менторский тон, а у меня в таких случаях пропадает желание что-либо обсуждать.

Вы привели некорректный пример и не можете этого признать. Это объективно. Я только это озвучил — ко мне то какие вопросы?

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


Детский сад, штаны на лямках. Учитесь признавать собственные ошибки.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[11]: Зачем проверять на неравенство this в операторе присваивания?
От: bydlocoder  
Дата: 03.05.14 15:01
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>>>Не может. Если есть clear_me, то никакого конструирования во временный объект и swap не будет. Будет вызов copy_from. Что вы и продемонстрировали своим примером в другом сообщении.

B>>Вы, похоже, ищете повод, чтобы придраться к примеру. Можно было еще сказать, что без функции main() не скомпилируется.
B>>Совершенно очевидно, это пример не про swap, он был приведен не как пример кода для компиляции, а как концепция.

AS>Претензия была не к компилируемости, а к концепции. Там, где концепция была корректна (см выше), претензий не было.


AS>Вы привели некорректный пример и не можете этого признать. Это объективно.


Да, первый пример некорректен. Вам легче? Но вы смоими комментариями продемонстрировали (а) его непонимание и (б) неприкрытое хамство. Это не менее объективно.

AS>Я только это озвучил — ко мне то какие вопросы?

К вам не может быть никаких вопросов, ваш уровень понятен. Засим прекращаю эту контрпродуктивную дискуссию.
Re[7]: Зачем проверять на неравенство this в операторе присваивания?
От: Patalog Россия  
Дата: 03.05.14 20:43
Оценка:
Здравствуйте, Andrew S, Вы писали:

P>>Не догоняю, зачем здесь using?


AS>Обычный lifehack.

AS>В результате код одинаково работоспособен в любом из кейзов: как при наличии мембера swap, внешней функции swap в неймспейсе типа, перегрузки std::swap, стандартной реализации std::swap, причем всегда автоматически выбирается наиболее специализированный вариант реализации.

Дык

// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.

же!!111
Почетный кавалер ордена Совка.
Re[8]: Зачем проверять на неравенство this в операторе присваивания?
От: Andrew S Россия http://alchemy-lab.com
Дата: 05.05.14 20:43
Оценка:
P>>>Не догоняю, зачем здесь using?

AS>>Обычный lifehack.

AS>>В результате код одинаково работоспособен в любом из кейзов: как при наличии мембера swap, внешней функции swap в неймспейсе типа, перегрузки std::swap, стандартной реализации std::swap, причем всегда автоматически выбирается наиболее специализированный вариант реализации.

P>Дык

P>

P>// swap должен быть перегружен для X и не кидать исключения. Иначе получим рекурсию. А если перегружен — то не получим ошибку, за исключением выше.

P>же!!111

Если речь все про зачем using — чтобы не задумываться о том, откуда брать реализацию, за нас это решит ADL.
Ровно так же корректно, если в классе есть нестатический мембер swap, было бы написать swap(other), но в оригинальном варианте был вызов внешней (ну или статической) функции swap, что я и оставил. В целом, это скорее просто моя личная привычка давать возможность не перегружать стандарный swap, а написать свой в неймспейсе класса — не всегда имеется возможность или желание сделать такую перегрузку. В реализации оператора = оно, наверное, особо смысла не несет, по крайней мере, сильно меньший, чем просто в клиенском коде.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.