Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.129.25.80] |
|
Страницы: (2) 1 [2] все ( Перейти к последнему сообщению ) |
Прикр. сообщ.
#1
,
|
|
|
Для тех, кто не в теме, краткая выдержка. При необходимости скопировать базовый подобъект из одного экземпляра в другой (возможно, другого класса, но базовый подобъект того же), у ТС возникла проблема из-за ошибочного каста левого операнда operator= к rvalue вместо lvalue. Вопрос был решён во втором же посту. Исходный топик тут. Затем в тему пришёл yuriunknown, и понеслось.
|
Сообщ.
#16
,
|
|
|
Продолжение.
static_cast<>. Цитата 5.2.9 Static cast И снова база. Неинтересно, но познавательно.1. The result of the expression static_cast<T>(v) is the result of converting the expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue. The static_cast operator shall not cast away constness (5.2.11). Цитата 5.2.9 Static cast Описывается нисходящее приведение ссылок. Которое оказывается полностью допустимым, а результат соответственно правильным, если целевой потомок действительно включает исходный базовый подообъект. Однако в случае нарушения этого правила получаем неопределённое поведение вместо фэйла. И кроме того, таким образом нельзя выполнить каст из виртуальной базы целевого потомка, т.к. пункт не применяется.2. An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The result has type “cv2 D.” An xvalue of type “cv1 B” may be cast to type “rvalue reference to cv2 D” with the same constraints as for an lvalue of type “cv1 B.” If the object of type “cv1 B” is actually a subobject of an object of type D, the result refers to the enclosing object of type D. Otherwise, the result of the cast is undefined. Цитата 5.2.9 Static cast Восходящий каст ссылок. На всякий случай посмотрим сюда:3. A glvalue of type “cv1 T1” can be cast to type “rvalue reference to cv2 T2” if “cv2 T2” is reference-compatible with “cv1 T1” (8.5.3). The result refers to the object or the specified base class subobject thereof. If T2 is an inaccessible (Clause 11) or ambiguous (10.2) base class of T1, a program that necessitates such a cast is ill-formed. Цитата 8.5.3 References чтобы убедиться, что при восходящем приведении правила для ссылок в целом не отличаются от правил для указателей, до которых мы со временем дойдём.4. Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2. ... Цитата 5.2.9 Static cast Если исходным и целевым типом являются указатели, то применимость пункта зависит от применимости 8.5 Initializers к этой паре типов. Смотреть? Ну конечно смотреть. Смотрим.4. Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The expression e is used as a glvalue if and only if the initialization uses it as a glvalue. Цитата 8.5 Initializers Выделенный вариант как раз тот, который упоминается в вышеуказанном пункте static_cast<>.1. A declarator can specify an initial value for the identifier being declared. The identifier designates a variable being initialized. The process of initialization described in the remainder of 8.5 applies also to initializations specified by other syntactic contexts, such as the initialization of function parameters with argument expressions (5.2.2) or the initialization of return values (6.6.3). initializer:
brace-or-equal-initializer
( expression-list ) ... тут остальные варианты, не имеющие отношения к рассматриваемому случаю Цитата 8.5 Initializers Ну что ж, constexpr в вышеуказанном пункте static_cast<> отсутствует, посему продолжаем. Далее следуют определения терминов. Неинтересно в рассматриваемом контексте, так же как неинтересны и пункты, не относящиеся к указателям или имеющим пустой expression-list. Так что продолжим с2. Except for objects declared with the constexpr specifier, for which see 7.1.5, an initializer in the definition of a variable can consist of arbitrary expressions involving literals and previously declared variables and functions, regardless of the variable’s storage duration. Цитата 8.5 Initializers Первый случай как раз наш.15. The initialization that occurs in the forms T x(a); T x{a}; as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization. Цитата 8.5 Initializers Ну наконец-то. Итак, следует посмотреть в ещё один пункт. Вот он16. The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. ... не наш случай. Цитата 4.10 Pointer conversions Предыдущие два пункта не относятся к нашему случаю. В итоге имеем, что безусловно позволяется инициализировать типы указателей значениями их собственных типов, а также использовать значения указателей на производные классы для инициализации указателей на базовые при условии доступности и однозначности последних. Ну и в окончательном итоге получаем, что вышеуказанный пункт static_cast<> применим при касте указателей на производные классы в указатели на доступные однозначные базовые.3. A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type. Продолжение следует. |
Сообщ.
#18
,
|
|
|
Затроллили пацана, теперь он уйдет с ресурса.
|
Сообщ.
#19
,
|
|
|
Продолжение.
Продолжаем за static_cast<>. Цитата 5.2.9 Static cast Итак, ищем ещё варианты применимости static_cast<>.5. Otherwise, the static_cast shall perform one of the conversions listed below. No other conversion shall be performed explicitly using a static_cast. Цитата 5.2.9 Static cast Просто один вопрос: dynamic_cast<> так умеет? Вопрос риторический. Наконец6. Any expression can be explicitly converted to type cv void. ... 7. The inverse of any standard conversion sequence (Clause 4) not containing an lvalue-to-rvalue (4.1), array-to-pointer (4.2), function-to-pointer (4.3), null pointer (4.10), null member pointer (4.11), or boolean (4.12) conversion, can be performed explicitly using static_cast. ... 8. The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) conversions are applied to the operand. Such a static_cast is subject to the restriction that the explicit conversion does not cast away constness (5.2.11), and the following additional rules for specific cases: 9. A value of a scoped enumeration type (7.2) can be explicitly converted to an integral type. ... 10. A value of integral or enumeration type can be explicitly converted to an enumeration type. ... 12. A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B” of type cv2 T, where ... 13. A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T,” where ... Цитата 5.2.9 Static cast То же самое, что уже рассмотренный пункт 2, но в применении к указателям, а не ссылкам.11. A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined. В итоге если сравнить, как работают static_cast<> и dynamic_cast<> при восходящем приведении указателей и ссылок, мы не обнаружим никаких отличий. Вообще. Те же ошибки, та же реакция на них, те же правила, те же результаты. Внимание, вопрос: зачем dynamic_cast<> наделяли такой избыточностью? Внимание, ответ: для единообразия. Странно было бы запрещать для dynamic_cast<> восходящее приведение, если нисходящее не запрещено для static_cast<>. Внимание, вопрос 2: почему гораздо более опасный нисходящий каст не запрещён для static_cast<>? Внимание, ответ 2: потому что нередко программист гарантирует его корректность иными методами, и заставлять его использовать исключительно чекающий способ нельзя. Это как если бы std::vector<> имел бы только std::vector<>::at() без std::vector<>::operator[](). Итого. dynamic_cast<> многого не умеет, что умеет static_cast<>. Он работает только с ссылками и указателями. Я не представляю, как программа будет выкручиваться из сей ситуации, кроме как 5.2.3 Explicit type conversion (functional notation) или 5.4 Explicit type conversion (cast notation), если её автор не любит static_cast<>. Если автор не изменит своего мнения, боюсь, я от него потребую объяснить, почему вместо static_cast<> он предпочитает небезопасные легаси-методы, при том что очень заботится о безопасных методах посредством dynamic_cast<>. При этом static_cast<> в принципе не умеет выполнять перекрёстные касты и кастить из виртуальных баз, а также не гарантирует корректности нисходящих кастов, т.к. в статике эту корректность проверить невозможно, поэтому результат на совести программиста. Т.о. безусловная ниша dynamic_cast<> – вот эти три случая, и то с оговоркой по последнему пункту. Мне даже стало интересно, и я посмотрел ассемблер: ptr_a = dynamic_cast<a*>(&B); ptr_a = static_cast<a*>(&B); ; Line 36 lea eax, DWORD PTR _B$[ebp] mov DWORD PTR _ptr_a$[ebp], eax ; Line 37 lea ecx, DWORD PTR _B$[ebp] mov DWORD PTR _ptr_a$[ebp], ecx Окончание следует. Добавлено Окончание. Цитата yuriunknown @ Покажи, где я неправильно прочитал Стандарт, и где, оказывается, Стандартом допускается невалидность результата при восходящих кастах при отсутствии диагностики во время компиляции. Покажи, где может быть ошибка. Пожалуйста.Qraizer а что мне пояснять, ты сделал проверку компилятором ещё на этапе сборки и зашил в код(я рад за тебя, чуть выше я показал что такой подход может привести к ошибке кастования), ... Цитата yuriunknown @ Он одобрил сказанное XandoX, потому что тот был первым. Я по-твоему настолько сноб, чтобы в довесок к способу решения за простое уточнение причины требовать каких-либо сатисфакций от кого-либо из них? Не суди по себе, уважаемый.... далее, если внимательно почитаешь сам топик, то увидишь что написал автор темы Цитата - т.е твой код уже повтороение того, что и так было известно автору, он просил пояснения по поводу брыка от компилятора на (a)B = C, ну и дальше по теме автор ничего указанногго тобой так и не одобрил.Варианты *(a*)&B = C и *static_cast<a*>(&B) = C; генерят необходимый код, но мне не понятно, чем они отличаются от (a)B = C и почему код (a)B = C выкидывается компилятором? Цитата yuriunknown @ Что я люблю, а что наоборот, не имеет никакого значения. Имеет значение наличие в программе бесполезного кода и флудоблюдство в тематике. Вон там чуть выше есть очень настоятельная просьба обосновать dynamic_cast<>. Милости прошу.Я решил предложить свой вариант, который будет гарантированно работать. Не любишь динамик каст - твои проблемы, а я статик каст не люблю. Цитата yuriunknown @ Избыточен – внезапно – избыточный код. Дефолтовый оператор присваивания делает ровно то, что нужно. Наличие у классов перегруженных операций копирования и присваивания ожидаемо, ибо это классы, и дефолтовые действия не всегда сохраняют инварианты. Наличие у структур перегруженных операций копирования и присваивания повергает в панику, т.к. класс без инвариантов скорее всего говорит о громадном невежестве автора кода.Касательно избыточности, т.е конструктор преобразования по твоёму не избыточен, а вот оператор копирования избыточен(NICE) На досуге почитай, что ли того же Пратту, там в самом начале есть что должно быть в интерфейсе класса, как раз некашерный для тебя оператор копирования делать рекомендовано. Спасибо, я предпочитаю более умных авторов. Например Саскинда или Хокинга. А если ограничиться IT, то умные люди пишут, что наличие перегруженных операций копирования и присваивания в программе, писанной под современный Стандарт языка, говорит скорее о непродуманности его архитектуры. Хотя случаи, конечно бывают сильно разные, и иногда в них есть ссылки, константы или внешние разделяемые ресурсы. Ну и конечно, если до сих пор юзать VC6, все случаи будут одинаковы. Цитата yuriunknown @ Внимание. Сейчас скажу страшную вещь. Protected-данные в классе никогда не требуются. Их наличие говорит... многое говорит, в общем, об авторе класса. Поправь, плз, потом продолжим. И смею напомнить, у автора топика были структуры, а не классы. Это так, на всякий случай.НО раз тут разговор зашёл об избыточности кода то ок, я использую дефолт (для интерфейса базового класса в шапке темы он вполне уместен, кстати для варианта со структурой тоже) http://codepad.org/ePQFTVXA и что, у кого теперь больше крючков в коде и чей код избыточен? Цитата yuriunknown @ Qraizer слющаааай, а как ты будешь поступать если в базовом классе будет довольно много полей вместо одного int a(вот давай введём штук 16-ть разнородных полей простых типов - там вперемешку int float double), мне вот интересно : ну понятно мой код с динамик кастом сработает, а что будешь деать ты, будь добр дай ответ "гулпому падавану" в моём лице Цитата Flex Ferrum @ Я думаю, что Qraizer применит Rule Of Zero, и по барабану, сколько там полей в базовом/базовых классах. |
Сообщ.
#20
,
|
|
|
По теме, ноль, сорри.
Топлю за Qraiser'а - в Цэ++он афигенен! |
Сообщ.
#21
,
|
|
|
Гыгы, по колено в коде.
кастовать динамик_кастом от производного к базовому - Цитата Qraizer @ Мне даже стало интересно, и я посмотрел ассемблер: ptr_a = dynamic_cast<a*>(&B); ptr_a = static_cast<a*>(&B); ; Line 36 lea eax, DWORD PTR _B$[ebp] mov DWORD PTR _ptr_a$[ebp], eax ; Line 37 lea ecx, DWORD PTR _B$[ebp] mov DWORD PTR _ptr_a$[ebp], ecx Плохой оптимизатор, первый кусок мог бы просто выбросить. |
Сообщ.
#22
,
|
|
|
Цитата applegame @ Так там нет оптимизатора, поэтому не мог. Плохой оптимизатор, первый кусок мог бы просто выбросить. |