Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.97.248] |
|
Прикр. сообщ.
#1
,
|
|
|
Для тех, кто не в теме, краткая выдержка. При необходимости скопировать базовый подобъект из одного экземпляра в другой (возможно, другого класса, но базовый подобъект того же), у ТС возникла проблема из-за ошибочного каста левого операнда operator= к rvalue вместо lvalue. Вопрос был решён во втором же посту. Исходный топик тут. Затем в тему пришёл yuriunknown, и понеслось.
|
Сообщ.
#1
,
|
|
|
Dushevny во первых в классе А необходм оператор копирования и конструктор копирования, во вторых НЕ (a)B = C а (a)B = (a)C но в данном случае создатся два временных объекта (на базе С и на базе В), поэтому пользы мало
Вбей данный код, сам всё поймёшь #include <iostream> using namespace std; class a { protected: int A; public: a() : A(0) { cout<<"DC : A OBJ CREATED : "<<(size_t)(this)<<endl; } a(const a &_copy){ cout<<"CC : A OBJ CREATED : "<<(size_t)(this)<<endl; A = _copy.A; } a & operator =(const a &_copy){ A = _copy.A; return *this; } int data() const { return A; } }; class b: public a { protected: int B; public: b() : B(1) { a::A = B; } }; class c: public a { protected: int C; public: c() : C(2) { a::A = C; } }; int main(){ b B; c C; (a)B = (a)C; cout<<B.data()<<endl; return 0; } Чтобы заработало как надо либо снабди интерфейсы классов необходимыми операторами копирования, либо используй вариант ((a *)&B)[0] = (a)C; Моя рекомендация использовать virtual наследование и dynamic_cast с разыменовыванием указателей #include <iostream> using namespace std; class a { protected: int A; public: a() : A(0) { cout<<"DC : A OBJ CREATED : "<<(size_t)(this)<<endl; } a(const a &_copy){ cout<<"CC : A OBJ CREATED : "<<(size_t)(this)<<endl; A = _copy.A; } a & operator =(const a &_copy){ A = _copy.A; return *this; } int data() const { return A; } virtual ~a(){} }; class b: public a { protected: int B; public: b() : B(1) { a::A = B; } }; class c: public a { protected: int C; public: c() : C(2) { a::A = C; } }; int main(){ b B; c C; a * ptrB = dynamic_cast<a *>(&B); a * ptrC = dynamic_cast<a *>(&C); if( ptrB &&ptrC ) *ptrB = *ptrC; cout<<B.data()<<endl; return 0; } Добавлено static_cast, не рекомендую так как не будет чеков в рантайме, а это как показывает практика приводит к ошибкам кастования. Это сообщение было перенесено сюда или объединено из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#2
,
|
|
|
yuriunknown, заранее извиняюсь, кто тебя языку учил?
Это сообщение было перенесено сюда или объединено из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#3
,
|
|
|
Qraizer, что именно подразумеваешь своим вопросом?И касательно какого языка, (разговорного или алгоритмического) возникли вопросы?
Это сообщение было перенесено сюда или объединено из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#4
,
|
|
|
yuriunknown, Для каста указателя или ссылки к указателю/ссылке на базовый класс нет смысла использовать dinamic_cast. Он всё равно заменяется на static_cast самим компилятором.
dinamic_cast предназначен для проверки на объект какого именно производного класса ссылается указатель базового и для соответствующего преобразования указателя. Это сообщение было перенесено сюда или объединено из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#5
,
|
|
|
amk слушай я не имею ничего против тебя только не учи меня букварю, ладно?
#include <iostream> using namespace std; class a { protected: size_t A; public : a() : A(0){} virtual ~a(){} }; class b : public a{ protected: size_t B; public: b() : B(1){} }; int main(){ a *arr[] = {0, 0}; arr[0] = new a(); arr[1] = new b(); b * ptr1 = static_cast < b *>(arr[0]); b * ptr2 = dynamic_cast< b *>(arr[1]); b * ptr3 = dynamic_cast< b *>(arr[0]); cout<<hex<<ptr1<<endl; cout<<hex<<ptr2<<endl; cout<<hex<<ptr3<<endl; return 0; } http://codepad.org/su1MMDtA Добавлено static_cast - в неумелых руках - ЗЛО в первом лице. Тоже касается cons_cast - а, dynamic_cast позволяет 100% знать какой виртуальный потомок из дерева наследования в данный момент в программе. Я закончил. Адишн : Он всё равно заменяется на static_cast самим компилятором. - не пиши так больше. Сообщения были разделены в тему "Касты в статикее и динамике" Это сообщение было перенесено сюда или объединено из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#6
,
|
|
|
Я же извинился, да? Ну да, точно.
Цитата yuriunknown @ Тут раздел по С++. Впрочем, неуважение к русскому тоже не приветствуется.И касательно какого языка, (разговорного или алгоритмического) возникли вопросы? Первый приведённый код вообще о чём был? Если о том, что уже разъяснено в сообщении №2, то он слегка припоздал. А если о том, что требуется приведение типа для правого операнда, то он демонстрирует только избыточность как самого каста, так и перегрузки operator=(). И никаких временных объектов. Цитата yuriunknown @ Интересно, как же тогда в C копировали структуры обычным присваиванием, если для этого нужно обязательно виртуальный базовый класс и проверки типов в run-time. Кстати, что-то я не вижу во втором приведённом коде виртуальных базовых классов.Моя рекомендация использовать virtual наследование и dynamic_cast с разыменовыванием указателей Цитата yuriunknown @ Если в run-time не будет избыточных действий, программа от этого только выиграет. Ошибки кастов при восходящем приведении типов могут быть только, если обманывать компилятор. static_cast, не рекомендую так как не будет чеков в рантайме, а это как показывает практика приводит к ошибкам кастования. Добавлено Цитата yuriunknown @ В букваре написано, что любой указатель или ссылка на производный класс может быть приведён к указателю или соответственно ссылке на доступный однозначный базовый класс. Цитировать Стандарт надо? Как-никак, не букварь, однако. amk слушай я не имею ничего против тебя только не учи меня букварю, ладно? Добавлено Помимо этого в букваре также должно быть разъяснено, чем отличается нисходящий каст от восходящего, а также перекрёстного. Эта тема была разделена из темы "приведение lvalue к типу предка работает не так, как я ожидаю" |
Сообщ.
#7
,
|
|
|
Qraizer я немного не понял может мне уйти с этого ресурса?Как я вижу, кроме культа личности(не будем показывать на аватр выше) тут не дорожат участниками.
Можно было прямо написать или нет?Ну так скажи я пошлю и этот форум. Поясни мне глупому твой до*б. ЗЫ: А может тебе не даёт покоя мой АйПишник, верней откуда он, ну так напиши конкретно(можешь в ЛС) что это за почести от тебя в мою сторону пошли? Добавлено Цитата - ну так увидь, плохо значит смотришь. На счёт Си - давай не будем мешать процедурный и объектно ориентированный язык, ладно? Интересно, как же тогда в C копировали структуры обычным присваиванием, если для этого нужно обязательно виртуальный базовый класс и проверки типов в run-time. Кстати, что-то я не вижу во втором приведённом коде виртуальных базовых классов. |
Сообщ.
#8
,
|
|
|
yuriunknown, Qraizer просто указывает на допущенные тобой неточности. А он здесь, пожалуй один из десятка (даже меньше), кто реально знает стандарт языка. Ну или почти знает. Мне по крайней мере до него далеко. Так что к его мнению в этом вопросе лучше прислушаться, а не вступать в бессмысленный спор.
Цитата yuriunknown @ Тут речь шла не столько о плюсах, сколько об реализации протокола обмена. То есть топикстартеру требуется получить не объект с неважно какими внутренностями, а вполне определённую последовательность байт (если не бит). В таких случаях волей-неволей приходится откатываться к использованию средств, пришедших в язык из его предка. И классы с виртуальным наследованием здесь вообще не катят, поскольку их внутренней структурой нельзя управлять простыми средствами. ЗЫ: Чисто для себя - причём к объектно ориентированному языку процедурный?(Тут речь шла о плюсах) |
Сообщ.
#9
,
|
|
|
amk
Цитата - где сие было упомянуто? о есть топикстартеру требуется получить не объект с неважно какими внутренностями, а вполне определённую последовательность байт (если не бит). |
Сообщ.
#10
,
|
|
|
yuriunknown, поступай, как будет угодно. Участникам на этом ресурсе всегда рады, когда они создают полезный контент. Хотя бы капельку полезный. Я не виноват, что ты уже второй раз заходишь в чью-то уже решённую тему и устраиваешь балаган из наполовину очевидных, наполовину ложных тезисов. Вопрос Dushevny-а создал полезного контента больше. Третий раз увидеть балаган в ещё одной теме я не хочу, это правда.
Добавлено Если не согласен с критикой, потрудись объяснить поведение кода по ссылке из моего поста выше. Результат там противоречит твоим утверждениям. Твои утверждения касательно static_cast<> не согласуются с гарантиями Стандарта языка. Цитаты могу привести. Твои примеры, якобы подтверждающие твою правоту касательно dynamic_cast<>, относятся к совершенно другой предметной области, не рассматриваемой в этой теме. В конце концов ты первый оскорбил amk, который, судя по твоим постам, язык знает куда лучше тебя. Добавлено Цитата yuriunknown @ Я один плохо смотрю, или ещё есть? Вопрос не напотроллить, ибо наличие виртуальных баз как раз может служить причиной, почему вместо static_cast<> в восходящем касте может потребоваться dynamic_cast<>. А может и не потребоваться. Но может потребоваться. так что вопрос важный. Кто-нибудь вообще, кроме yuriunknown, видит там хоть один виртуальный базовый класс? Цитата - ну так увидь, плохо значит смотришь....Кстати, что-то я не вижу во втором приведённом коде виртуальных базовых классов. |
Сообщ.
#11
,
|
|
|
Qraizer а что мне пояснять, ты сделал проверку компилятором ещё на этапе сборки и зашил в код(я рад за тебя, чуть выше я показал что такой подход может привести к ошибке кастования), далее, если внимательно почитаешь сам топик, то увидишь что написал автор темы
Цитата - т.е твой код уже повтороение того, что и так было известно автору, он просил пояснения по поводу брыка от компилятора на (a)B = C, ну и дальше по теме автор ничего указанногго тобой так и не одобрил. Я решил предложить свой вариант, который будет гарантированно работать. Не любишь динамик каст - твои проблемы, а я статик каст не люблю. Варианты *(a*)&B = C и *static_cast<a*>(&B) = C; генерят необходимый код, но мне не понятно, чем они отличаются от (a)B = C и почему код (a)B = C выкидывается компилятором? Касательно избыточности, т.е конструктор преобразования по твоёму не избыточен, а вот оператор копирования избыточен(NICE) На досуге почитай, что ли того же Пратту, там в самом начале есть что должно быть в интерфейсе класса, как раз некашерный для тебя оператор копирования делать рекомендовано. НО раз тут разговор зашёл об избыточности кода то ок, я использую дефолт (для интерфейса базового класса в шапке темы он вполне уместен, кстати для варианта со структурой тоже) http://codepad.org/ePQFTVXA и что, у кого теперь больше крючков в коде и чей код избыточен? Qraizer слющаааай, а как ты будешь поступать если в базовом классе будет довольно много полей вместо одного int a(вот давай введём штук 16-ть разнородных полей простых типов - там вперемешку int float double), мне вот интересно : ну понятно мой код с динамик кастом сработает, а что будешь деать ты, будь добр дай ответ "гулпому падавану" в моём лице |
Сообщ.
#12
,
|
|
|
Ок, yuriunknown. Поехали по-серьёзному.
Приступим. dynamic_cast<>. Цитата 5.2.7 Dynamic cast Это база. Если нарушаются эти пункты, компилятор регистрирует семантическую ошибку. Далее.1. The result of the expression dynamic_cast<T>(v) is the result of converting the expression v to type T. T shall be a pointer or reference to a complete class type, or “pointer to cv void.” The dynamic_cast operator shall not cast away constness (5.2.11). 2. If T is a pointer type, v shall be a prvalue of a pointer to complete class type, and the result is a prvalue of type T. If T is an lvalue reference type, v shall be an lvalue of a complete class type, and the result is an lvalue of the type referred to by T. If T is an rvalue reference type, v shall be an expression having a complete class type, and the result is an xvalue of the type referred to by T. Цитата 5.2.7 Dynamic cast Тривиальные случаи. При касте с устрожением квалификации квалификация добавляется, но в любом случае результат ссылается на исходный операнд. Каст пустого указателя оставляет результат пустым.3. If the type of v is the same as T, or it is the same as T except that the class object type in T is more cv-qualified than the class object type in v, the result is v (converted if necessary). 4 If the value of v is a null pointer value in the pointer case, the result is the null pointer value of type T. Цитата 5.2.7 Dynamic cast Наиболее интересный случай. Каст к базе, в случае если база единственна и доступна, операнд приводится к целевому типу. В случае неоднозначности или недоступности компилятор регистрирует грамматическую ошибку. В случае отсутствия целевой базы у операнда, пункт не применяется.5 If T is “pointer to cv1 B” and v has type “pointer to cv2 D” such that B is a base class of D, the result is a pointer to the unique B subobject of the D object pointed to by v. Similarly, if T is “reference to cv1 B” and v has type cv2 D such that B is a base class of D, the result is the unique B subobject of the D object referred to by v. 65 The result is an lvalue if T is an lvalue reference, or an xvalue if T is an rvalue reference. In both the pointer and reference cases, the program is ill-formed if cv2 has greater cv-qualification than cv1 or if B is an inaccessible or ambiguous base class of D. ... тут ещё пример 65) The most derived object (1.8) pointed or referred to by v can contain other B objects as base classes, but these are ignored. Цитата 5.2.7 Dynamic cast Оппа. Только теперь Стандарт требует полиморфности операнда. Интересно. Но идём дальше.6. Otherwise, v shall be a pointer to or an lvalue of a polymorphic type (10.3). Цитата 5.2.7 Dynamic cast Совсем занимательно. Только сейчас программе требуется определение самого производного объекта, и если целью не является указатель на ничто, то дополнительно ещё и некие run-time проверки. "Внезапно" © выясняется, что до этого run-time проверок не требовалось.7 If T is “pointer to cv void,” then the result is a pointer to the most derived object pointed to by v. Otherwise, a run-time check is applied to see if the object pointed or referred to by v can be converted to the type pointed or referred to by T. Цитата 5.2.7 Dynamic cast Первый элемент списка описывает нисходящие приведения, второй – перекрёстные. А третий запрещает остальные случаи. Как? А вот так:8. If C is the class type to which T points or refers, the run-time check logically executes as follows: Цитата 5.2.7 Dynamic cast Ожидаемо, не правда ли? Именно так описывают dynamic_cast<> в букварях. Правда, там не всегда пишут, что делает dynamic_cast<> при восходящих и тривиальных кастах. А оказывается, что Стандарт описывает в таких случаях самые обычные статичные проверки, выполняемые компилятором. Который если при компиляции не обнаружит нарушений, в принципе неспособен сфэйлить каст. Нет, он, конечно, имеет полное право выполнить подобный каст в run-time, если дурак, но фэйлов не будет, раз уж компиляция прошла успешно.9. The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws std::bad_cast (18.7.2). Продолжение следует. |
Сообщ.
#13
,
|
|
|
Ух... Как толсто то! Я то думал, что ЙА-шки только на фикбуке водятся, а оно вона как!
Добавлено Цитата yuriunknown @ На досуге почитай, что ли того же Пратту Я бы предложил вам почитать что-нибудь более основательное и авторитетное. Если Страуструп слишком объёмен и сложен, то Саттер с его "сложными задачами" - самое оно. Детально разжевывает предмет дискуссии. Ну, ещё можно погуглить на предмет Rule of Zero, Rule of Five, Rule of Three. Мне вот гугл первой же ссылкой выдал нужную страницу с cppreference.com. Добавлено Цитата yuriunknown @ Qraizer слющаааай, а как ты будешь поступать если в базовом классе будет довольно много полей вместо одного int a(вот давай введём штук 16-ть разнородных полей простых типов - там вперемешку int float double), мне вот интересно : ну понятно мой код с динамик кастом сработает, а что будешь деать ты, будь добр дай ответ "гулпому падавану" в моём лице Я думаю, что Qraizer применит Rule Of Zero, и по барабану, сколько там полей в базовом/базовых классах. |
Сообщ.
#14
,
|
|
|
Не уверен как в том, что тут есть о чем холиварить, так и в том, что виновник торжества будет здесь отвечать.
|
Сообщ.
#15
,
|
|
|
Продолжение.
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. Продолжение следует. |
Сообщ.
#16
,
|
|
|
Цитата D_KEY @ +1. Не уверен ... в том, что виновник торжества будет здесь отвечать. |
Сообщ.
#17
,
|
|
|
Затроллили пацана, теперь он уйдет с ресурса.
|
Сообщ.
#18
,
|
|
|
Продолжение.
Продолжаем за 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, и по барабану, сколько там полей в базовом/базовых классах. |
Сообщ.
#19
,
|
|
|
По теме, ноль, сорри.
Топлю за Qraiser'а - в Цэ++он афигенен! |
Сообщ.
#20
,
|
|
|
Гыгы, по колено в коде.
кастовать динамик_кастом от производного к базовому - Цитата 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 Плохой оптимизатор, первый кусок мог бы просто выбросить. |
Сообщ.
#21
,
|
|
|
Цитата applegame @ Так там нет оптимизатора, поэтому не мог. Плохой оптимизатор, первый кусок мог бы просто выбросить. |