На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (2) 1 [2]  все  ( Перейти к последнему сообщению )  
> Касты в статике и динамике , "больше" значит "лучше"?
    Для тех, кто не в теме, краткая выдержка. При необходимости скопировать базовый подобъект из одного экземпляра в другой (возможно, другого класса, но базовый подобъект того же), у ТС возникла проблема из-за ошибочного каста левого операнда operator= к rvalue вместо lvalue. Вопрос был решён во втором же посту. Исходный топик тут. Затем в тему пришёл yuriunknown, и понеслось.
      Продолжение.

      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
      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 к этой паре типов. Смотреть? Ну конечно смотреть. Смотрим.
      Цитата 8.5 Initializers
      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 )

      ... тут остальные варианты, не имеющие отношения к рассматриваемому случаю
      Выделенный вариант как раз тот, который упоминается в вышеуказанном пункте static_cast<>.
      Цитата 8.5 Initializers
      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.
      Ну что ж, constexpr в вышеуказанном пункте static_cast<> отсутствует, посему продолжаем. Далее следуют определения терминов. Неинтересно в рассматриваемом контексте, так же как неинтересны и пункты, не относящиеся к указателям или имеющим пустой expression-list. Так что продолжим с
      Цитата 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. ... не наш случай.
      • If the initializer is a braced-init-list... не наш случай.
      • If the destination type is a reference type... не наш случай.
      • If the destination type is an array of characters, an array of char16_t, an array of char32_t, or an array of wchar_t, and... не наш случай.
      • If the initializer is ()... не наш случай.
      • Otherwise, if the destination type is an array... не наш случай.
      • If the destination type is a (possibly cv-qualified) class type:
        ... не наш случай.
      • Otherwise, if the source type is a (possibly cv-qualified) class type... не наш случай.
      • 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. ... примечание о независимости квалификаторов и пример неинтересны.
      Ну наконец-то. Итак, следует посмотреть в ещё один пункт. Вот он
      Цитата 4.10 Pointer conversions
      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.
      Предыдущие два пункта не относятся к нашему случаю. В итоге имеем, что безусловно позволяется инициализировать типы указателей значениями их собственных типов, а также использовать значения указателей на производные классы для инициализации указателей на базовые при условии доступности и однозначности последних. Ну и в окончательном итоге получаем, что вышеуказанный пункт static_cast<> применим при касте указателей на производные классы в указатели на доступные однозначные базовые.

      Продолжение следует.
      Сообщение отредактировано: Qraizer -
        Цитата D_KEY @
        Не уверен ... в том, что виновник торжества будет здесь отвечать.
        +1. :yes-sad:
          Затроллили пацана, теперь он уйдет с ресурса.
            Продолжение.

            Продолжаем за static_cast<>.
            Цитата 5.2.9 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.
            Итак, ищем ещё варианты применимости static_cast<>.
            Цитата 5.2.9 Static 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 ...
            Просто один вопрос: dynamic_cast<> так умеет? Вопрос риторический. Наконец
            Цитата 5.2.9 Static cast
            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.
            То же самое, что уже рассмотренный пункт 2, но в применении к указателям, а не ссылкам.

            В итоге если сравнить, как работают 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<> – вот эти три случая, и то с оговоркой по последнему пункту.
            Мне даже стало интересно, и я посмотрел ассемблер:
            ExpandedWrap disabled
                  ptr_a = dynamic_cast<a*>(&B);
                  ptr_a = static_cast<a*>(&B);
            ExpandedWrap disabled
              ; 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 @
            ... далее, если внимательно почитаешь сам топик, то увидишь что написал автор темы
            Цитата
            Варианты *(a*)&B = C и *static_cast<a*>(&B) = C; генерят необходимый код, но мне не понятно, чем они отличаются от (a)B = C и почему код (a)B = C выкидывается компилятором?
            - т.е твой код уже повтороение того, что и так было известно автору, он просил пояснения по поводу брыка от компилятора на (a)B = C, ну и дальше по теме автор ничего указанногго тобой так и не одобрил.
            Он одобрил сказанное XandoX, потому что тот был первым. Я по-твоему настолько сноб, чтобы в довесок к способу решения за простое уточнение причины требовать каких-либо сатисфакций от кого-либо из них? Не суди по себе, уважаемый.
            Цитата yuriunknown @
            Я решил предложить свой вариант, который будет гарантированно работать. Не любишь динамик каст - твои проблемы, а я статик каст не люблю.
            Что я люблю, а что наоборот, не имеет никакого значения. Имеет значение наличие в программе бесполезного кода и флудоблюдство в тематике. Вон там чуть выше есть очень настоятельная просьба обосновать dynamic_cast<>. Милости прошу.
            Цитата yuriunknown @
            Касательно избыточности, т.е конструктор преобразования по твоёму не избыточен, а вот оператор копирования избыточен(NICE) :D На досуге почитай, что ли того же Пратту, там в самом начале есть что должно быть в интерфейсе класса, как раз некашерный для тебя оператор копирования делать рекомендовано.
            Избыточен – внезапно – избыточный код. Дефолтовый оператор присваивания делает ровно то, что нужно. Наличие у классов перегруженных операций копирования и присваивания ожидаемо, ибо это классы, и дефолтовые действия не всегда сохраняют инварианты. Наличие у структур перегруженных операций копирования и присваивания повергает в панику, т.к. класс без инвариантов скорее всего говорит о громадном невежестве автора кода.
            Спасибо, я предпочитаю более умных авторов. Например Саскинда или Хокинга. А если ограничиться IT, то умные люди пишут, что наличие перегруженных операций копирования и присваивания в программе, писанной под современный Стандарт языка, говорит скорее о непродуманности его архитектуры. Хотя случаи, конечно бывают сильно разные, и иногда в них есть ссылки, константы или внешние разделяемые ресурсы. Ну и конечно, если до сих пор юзать VC6, все случаи будут одинаковы.
            Цитата yuriunknown @
            НО раз тут разговор зашёл об избыточности кода то ок, я использую дефолт (для интерфейса базового класса в шапке темы он вполне уместен, кстати для варианта со структурой тоже)
            http://codepad.org/ePQFTVXA и что, у кого теперь больше крючков в коде и чей код избыточен?
            Внимание. Сейчас скажу страшную вещь. Protected-данные в классе никогда не требуются. Их наличие говорит... многое говорит, в общем, об авторе класса. Поправь, плз, потом продолжим. И смею напомнить, у автора топика были структуры, а не классы. Это так, на всякий случай.
            Цитата yuriunknown @
            Qraizer слющаааай, а как ты будешь поступать если в базовом классе будет довольно много полей вместо одного int a(вот давай введём штук 16-ть разнородных полей простых типов - там вперемешку int float double), мне вот интересно : ну понятно мой код с динамик кастом сработает, а что будешь деать ты, будь добр дай ответ "гулпому падавану" в моём лице
            Цитата Flex Ferrum @
            Я думаю, что Qraizer применит Rule Of Zero, и по барабану, сколько там полей в базовом/базовых классах.
            Сообщение отредактировано: Qraizer -
              По теме, ноль, сорри.
              Топлю за Qraiser'а - в Цэ++он афигенен!
              Сообщение отредактировано: Flex Ferrum -
                Гыгы, по колено в коде.
                кастовать динамик_кастом от производного к базовому - :lool:
                Цитата Qraizer @
                Мне даже стало интересно, и я посмотрел ассемблер:
                ExpandedWrap disabled
                      ptr_a = dynamic_cast<a*>(&B);
                      ptr_a = static_cast<a*>(&B);
                ExpandedWrap disabled
                  ; 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
                Внезапно? Для кого как.

                Плохой оптимизатор, первый кусок мог бы просто выбросить. :)
                Сообщение отредактировано: applegame -
                  Цитата applegame @
                  Плохой оптимизатор, первый кусок мог бы просто выбросить.
                  Так там нет оптимизатора, поэтому не мог.
                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                  0 пользователей:


                  Рейтинг@Mail.ru
                  [ Script execution time: 0,0517 ]   [ 17 queries used ]   [ Generated: 29.03.24, 12:14 GMT ]