Ужас под заголовком C++11 FAQ
От: Caracrist https://1pwd.org/
Дата: 17.10.11 12:15
Оценка:
здесь
    class Widget {    // Three alternative implementations represented as a union
    private:
        enum class Tag { point, number, text } type;    // discriminant
        union {        // representation
            point p;      // point has constructor
            int i;
            string s;     // string has default constructor, copy operations, and destructor
        };
        // ...
        widget& operator=(const widget& w)     // necessary because of  the string variant
        {
            if (type==Tag::text && w.type==Tag::text) {
                s = w.s;        // usual string assignment
                return *this;
            }

            if (type==Tag::text) s.~string();    // destroy (explicitly!)

            switch (type==w.type) {
            case Tag::point: p = w.p; break;    // normal copy
            case Tag::number: i = w.i; break;
            case Tag::text: new(&s)(w.s); break;    // placement new
            }
            type = w.type;
            return *this;
        }
    };


Я выделил не понятные мне места.

point p; — это какого типа переменная? с моей точки зрения это равнозначно написанию:
42 р;

new(&s)(w.s); — такое в операторе присваивания, это бесплатное пособие по UB
~~~~~
~lol~~
~~~ Single Password Solution
Re: Ужас под заголовком C++11 FAQ
От: rus blood Россия  
Дата: 17.10.11 12:26
Оценка: +1
Здравствуйте, Caracrist, Вы писали:

Я не понял только вот это
C>
C>            switch (type==w.type) {
C>

Остальное не вызывает вопросов.
Имею скафандр — готов путешествовать!
Re: Ужас под заголовком C++11 FAQ
От: gegMOPO4  
Дата: 17.10.11 12:41
Оценка: 2 (1) +1
Здравствуйте, Caracrist, Вы писали:
C>Я выделил не понятные мне места.
C>point p; — это какого типа переменная? с моей точки зрения это равнозначно написанию:
C>42 р;

Некий пользовательский тип, имеющий пользовательский конструктор (но не деструктор). 42 не идентификатор и не может быть именем типа, поэтому не равнозначно.

C>new(&s)(w.s); — такое в операторе присваивания, это бесплатное пособие по UB


Нормально. У нас ведь не простая структура, а объединение. Когда переключаемся со строки, вызывается деструктор, когда переключаемся на строку — конструктор (напомню, что int — POD, а point не имеет пользовательского деструктора), в частном случае, если до и после строка — оптимизация присваиванием. Никакого UB.
Re[2]: Ужас под заголовком C++11 FAQ
От: B0FEE664  
Дата: 17.10.11 15:44
Оценка: +2
Здравствуйте, rus blood, Вы писали:

RB>Я не понял только вот это

C>>
C>>            switch (type==w.type) {
C>>

RB>Остальное не вызывает вопросов.
А я вот ещё думаю, что widget и Widget — это разные типы. Поэтому, оператор присваивания у меня вызывает подозрения.
И каждый день — без права на ошибку...
Re[2]: Ужас под заголовком C++11 FAQ
От: Юрий Жмеренецкий ICQ 380412032
Дата: 17.10.11 16:29
Оценка:
Здравствуйте, gegMOPO4, Вы писали:

C>>new(&s)(w.s); — такое в операторе присваивания, это бесплатное пособие по UB


MOP>Нормально. ... Никакого UB.


Как минимум — последовательность вызовов:

s.~string();

new(&s)(args); // (2)

не является exception-safe безотносительно того, где она располагается. Но здесь ситуация еще хуже — потенциальное исключение в строке 2 не просто оставит объект в несогласованном состоянии, но и приведет к повторному вызову деструктора для объекта 's' при уничтожении объекта типа 'Widget' (а это UB).
Re[3]: Ужас под заголовком C++11 FAQ
От: gegMOPO4  
Дата: 17.10.11 16:48
Оценка: +1
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Как минимум — последовательность вызовов:
ЮЖ>
ЮЖ>s.~string();

ЮЖ>new(&s)(args); // (2)

ЮЖ>не является exception-safe безотносительно того, где она располагается. Но здесь ситуация еще хуже — потенциальное исключение в строке 2 не просто оставит объект в несогласованном состоянии, но и приведет к повторному вызову деструктора для объекта 's' при уничтожении объекта типа 'Widget' (а это UB).

Нет. Как ни странно, этот хакерский код не содержит ошибок (не считая нескольких уже отмеченных опечаток).

Эта последовательность вызовов никогда не исполнится, случай type==Tag::text && w.type==Tag::text обработан отдельно (потому и без проверки на самоприсваивание можно обойтись). Если в строке (2) выскочит исключение, то объект останется в безопасном состоянии Tag::point или Tag::number.
Re[3]: Ужас под заголовком C++11 FAQ
От: wvoquine  
Дата: 18.10.11 06:03
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Как минимум — последовательность вызовов:


ЮЖ>
ЮЖ>s.~string();

ЮЖ>new(&s)(args); // (2)

ЮЖ>не является exception-safe безотносительно того, где она располагается.


Если конструктор ломается, то мы теряем предыдущее состояние?

Но тогда то же самое верно, даже если s.~string(); никогда не вызывается непосредственно перед new(&s)(args);

Или нет?
To be is to be the value of a variable
Re: Ужас под заголовком C++11 FAQ
От: PredatorAlpha  
Дата: 18.10.11 07:39
Оценка:
C>point p; — это какого типа переменная? с моей точки зрения это равнозначно написанию:
C>42 р;

Tag::point и point — неверно не одно и то же обозначает, да?
Re[4]: Ужас под заголовком C++11 FAQ
От: Caracrist https://1pwd.org/
Дата: 18.10.11 08:14
Оценка:
Здравствуйте, gegMOPO4, Вы писали:

MOP>Эта последовательность вызовов никогда не исполнится, случай type==Tag::text && w.type==Tag::text обработан отдельно (потому и без проверки на самоприсваивание можно обойтись). Если в строке (2) выскочит исключение, то объект останется в безопасном состоянии Tag::point или Tag::number.



new(&s)(args); // thrown exception, oops: type == Tag::text
type = w.type; // never reach this code
//next call 
if (type==Tag::text) s.~string(); // oops, UB
~~~~~
~lol~~
~~~ Single Password Solution
Re[2]: Ужас под заголовком C++11 FAQ
От: Caracrist https://1pwd.org/
Дата: 18.10.11 08:15
Оценка:
Здравствуйте, PredatorAlpha, Вы писали:

C>>point p; — это какого типа переменная? с моей точки зрения это равнозначно написанию:

C>>42 р;

PA>Tag::point и point — неверно не одно и то же обозначает, да?


да, я уже понял что прогнал с этим...
~~~~~
~lol~~
~~~ Single Password Solution
Re[5]: Ужас под заголовком C++11 FAQ
От: k.o. Россия  
Дата: 18.10.11 08:21
Оценка:
Здравствуйте, Caracrist, Вы писали:

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


MOP>>Эта последовательность вызовов никогда не исполнится, случай type==Tag::text && w.type==Tag::text обработан отдельно (потому и без проверки на самоприсваивание можно обойтись). Если в строке (2) выскочит исключение, то объект останется в безопасном состоянии Tag::point или Tag::number.



C>
C>new(&s)(args); // thrown exception, oops: type == Tag::text
C>type = w.type; // never reach this code
C>//next call 
C>if (type==Tag::text) s.~string(); // oops, UB
C>


При следующем вызове type, по прежнему, будет Tag::point или Tag::number, откуда UB?
Re[5]: Ужас под заголовком C++11 FAQ
От: wvoquine  
Дата: 18.10.11 08:29
Оценка: 2 (1)
Здравствуйте, Caracrist, Вы писали:

C>
C>new(&s)(args); // thrown exception, oops: type == Tag::text
C>


Так в том то и дело, что это случай отдельно обрабатывается, и то, что он тип не изменяет на текст — это хорошо.
Только вот что будет с теми данными, которые там до того хранились в соответствии с другим типом?
To be is to be the value of a variable
Re[6]: Ужас под заголовком C++11 FAQ
От: k.o. Россия  
Дата: 18.10.11 09:34
Оценка:
Здравствуйте, wvoquine, Вы писали:

W>Так в том то и дело, что это случай отдельно обрабатывается, и то, что он тип не изменяет на текст — это хорошо.

W>Только вот что будет с теми данными, которые там до того хранились в соответствии с другим типом?

Скорее всего, ничего хорошего ожидать не стоит, с другой стороны, никто и не обещал строгой гарантии безопасности исключений.
Re: Ужас под заголовком C++11 FAQ
От: Дрободан Фрилич СССР  
Дата: 18.10.11 15:33
Оценка: :)
Здравствуйте, Caracrist, Вы писали:

C>здесь

C>
C>    class Widget {    // Three alternative implementations represented as a union
C>    private:
C>        enum class Tag { point, number, text } type;    // discriminant
C>        union {        // representation
C>            point p;      // point has constructor
C>            int i;
C>            string s;     // string has default constructor, copy operations, and destructor
C>        };
C>        // ...
C>        widget& operator=(const widget& w)     // necessary because of  the string variant
C>        {
C>            if (type==Tag::text && w.type==Tag::text) {
C>                s = w.s;        // usual string assignment
C>                return *this;
C>            }

C>            if (type==Tag::text) s.~string();    // destroy (explicitly!)

C>            switch (type==w.type) {
C>            case Tag::point: p = w.p; break;    // normal copy
C>            case Tag::number: i = w.i; break;
C>            case Tag::text: new(&s)(w.s); break;    // placement new
C>            }
C>            type = w.type;
C>            return *this;
C>        }
C>    };
C>


C>Я выделил не понятные мне места.


C>point p; — это какого типа переменная? с моей точки зрения это равнозначно написанию:

C>42 р;

C>new(&s)(w.s); — такое в операторе присваивания, это бесплатное пособие по UB


А меня смущает enum class Tag.
Что это означает? Раньше такого не встречал...
Re[2]: Ужас под заголовком C++11 FAQ
От: Sir-G  
Дата: 18.10.11 17:48
Оценка:
Здравствуйте, rus blood, Вы писали:

RB>Я не понял только вот это

C>>
C>>            switch (type==w.type) {
C>>

Имхо должно быть switch (w.type) {
Re[2]: Ужас под заголовком C++11 FAQ
От: Abyx Россия  
Дата: 18.10.11 18:30
Оценка: 1 (1)
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>А меня смущает enum class Tag.

ДФ>Что это означает? Раньше такого не встречал...

в заголовке темы написано "С++11", почитайте стандарт.
In Zen We Trust
Re[2]: Ужас под заголовком C++11 FAQ
От: k.o. Россия  
Дата: 19.10.11 06:25
Оценка: 3 (2)
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>Здравствуйте, Caracrist, Вы писали:


ДФ>А меня смущает enum class Tag.

ДФ>Что это означает? Раньше такого не встречал...

Это из C++11, scoped enumeration. Основные отличия от обычных перечислений это необходимость явной квалификации значений перечисления и отсутствие неявных приведений к числовым типам.
enum class ScopedTag { tag1, tag2, tag3 }; 
enum UnscopedTag { unscopedTag1, unscopedTag2, unscopedTag3 }; 

ScopedTag x = ScopedTag::tag1; // ok
ScopedTag y = tag2; // error
int ix = x; // error
double dx = x; // error

UnscopedTag x_ = unscopedTag1; // ok
UnscopedTag y_ = unscopedTag2; // ok
int ix_ = x_; // ok
double dx_ = x_; // ok
Re[3]: Ужас под заголовком C++11 FAQ
От: Masterkent  
Дата: 19.10.11 17:50
Оценка:
B0FEE664:

BFE>Здравствуйте, rus blood, Вы писали:


RB>>Я не понял только вот это

C>>>
C>>>            switch (type==w.type) {
C>>>

RB>>Остальное не вызывает вопросов.
BFE>А я вот ещё думаю, что widget и Widget — это разные типы. Поэтому, оператор присваивания у меня вызывает подозрения.

Видимо, у Страуструпа copy-paste-илка сломалась. В N2544
switch (type_ = w.type_)

и widget всюду с маленькой буквы.

Кстати, пользы от этого новшества в C++ что-то не видно.
Re[4]: Ужас под заголовком C++11 FAQ
От: gegMOPO4  
Дата: 19.10.11 18:19
Оценка:
Здравствуйте, Masterkent, Вы писали:
M>Кстати, пользы от этого новшества в C++ что-то не видно.

А шоб було. Просто сняли перестраховочное ограничение, позволили легально стрелять в ногу.

Нужно это очень редко, когда критичны скорость и память (иначе можно было бы использовать обычный динамичный полиморфизм). Например, большой массив лексем в каком-то компиляторе. Или динамический числовой тип. Теперь в качестве кирпичиков можно использовать и готовые сложные типы. Последствия на совести программиста. Многим это не понадобится ни разу в жизни.
Re[5]: Ужас под заголовком C++11 FAQ
От: Masterkent  
Дата: 19.10.11 19:59
Оценка: 1 (1)
gegMOPO4:

M>>Кстати, пользы от этого новшества в C++ что-то не видно.


MOP>Нужно это очень редко, когда критичны скорость и память (иначе можно было бы использовать обычный динамичный полиморфизм).


Все эти хитрые манипуляции с памятью можно выполнять с помощью std::aligned_union или даже std::aligned_storage. Причём аналог boost::variant (который, в отличие от union-ов, достаточно высокоуровневый, чтобы им было удобно пользоваться), насколько я вижу, удобнее реализовывать именно через std::aligned_union, а не union-ы. Иными словами, для низкоуровневых операций вполне сгодится std::aligned_union, для высокоуровневых — какая-нибудь обёртка вокруг std::aligned_union. Чего ради надо было расширять возможности union-ов, непонятно (с учётом того, что std::aligned_union появилось раньше — см. N2369). Ещё интереснее то, что std::aligned_union собирались из стандарта выпилить, но потом одумались
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.