S>Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.
неприятности именно во время выполнения.
При виртуальном наследовании для того чтобы сделать static_cast из базового типа в производный во всех разумных реализациях происходит обращение к памяти объекта (потому что смещение нельзя сделать константным как при невиртуальном наследовании, поэтому недостаточно просто прибавить/отнять константу). То есть во время выполнения static_cast будет происходить обращение к памяти рядом с указателем, что для плохого указателя чревато.
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
S>>Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.
D>неприятности именно во время выполнения. D>При виртуальном наследовании для того чтобы сделать static_cast из базового типа в производный во всех разумных реализациях происходит обращение к памяти объекта (потому что смещение нельзя сделать константным как при невиртуальном наследовании, поэтому недостаточно просто прибавить/отнять константу). То есть во время выполнения static_cast будет происходить обращение к памяти рядом с указателем, что для плохого указателя чревато.
static_cast для этого случая не компилируется. Причины Вы указали — нужно обращение к памяти объекта в runtime. Пример я приводил:
#include <iostream>
using namespace std;
struct Base
{
virtual ~Base() {};
};
struct Derived : public virtual Base {};
int main()
{
Base* b = new Derived;
#if 0
Derived* d = dynamic_cast<Derived*>(b);
#else
Derived* d = static_cast<Derived*>(b);
#endif
cout << d0 << endl;
}
Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?
Re[13]: Можно ли определить, является ли тип наследником данного, на шаблонах?
Здравствуйте, Serg27, Вы писали:
S>Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?
Да, обратное. Его можно и static_cast'ом сделать, если охота...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Можно ли определить, является ли тип наследником данного, на шаблонах?
Здравствуйте, Serg27, Вы писали:
S>Мне действительно интересно — можно ли придумать ситуацию, когда static_cast указателя приведет к выбросу исключения. Вопрос о том что будет потом при использовании полученного указателя давайте обсуждать не будем — тут все понятно. Ну и такой должен сработать на современных распространенных архитектурах.
Собственно вот...
Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
На MSVC 8 всё так, как тебе хотелось -- GP внутри static_cast...
Только он там не обязвтельный, даже если его убрать и оставить неявное приведение типов, всё равно жахнет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: Можно ли определить, является ли тип наследником данного, на шаблонах?
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Serg27, Вы писали:
S>>Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?
E>Да, обратное. Его можно и static_cast'ом сделать, если охота...
Да, действительно: (здесь)
struct Base {};
struct D : public virtual Base {};
int main()
{
D* d = new D;
Base * b = static_cast<Base*>(d);
cout << b << endl;
d = NULL;
b = static_cast<Base*>(d);
cout << b << endl;
d = (D*)32;
cout << "d=" << d <<endl;
b = static_cast<Base*>(d);
cout << b << endl;
}
static_cast хоть и compile-time оператор, но лезет в этом случае за смещением на которое надо сдвинуть указатель в память. Ну значит получать указатель на виртуальную базу надо с большой осторожностью. Хорошо, что мне пришлось за всю жизнь всего один раз это использовать...
Re[14]: Можно ли определить, является ли тип наследником данного, на шаблонах?
E E>Собственно вот... E>Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
это означает, что загрузка сервера велика, и он все еще компилирует. У меня когда я зашел статус был — "успешно"
Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь
Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Здравствуйте, Serg27, Вы писали:
E>>Собственно вот... S>Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь
S>Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
J>И то, и другое придется переписывать, если, как ты там сказал, "понадобится добавить в иерархию ещё пяток классов, а из уже имеющихся 150, примерно 40 переписать".
Тогда переписывать может и не понадобиться...
Фишка же не в том, что бы формально не использовать визитёра, и как-то это дело закомуфлировать, а в том, что бы так спроектировать программу, что визитёр будет не нужен...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
. S>Из этой истории я себе хорошо запомнил, что в dynamic_cast надо пихать только валидные указатели, иначе именно в этом месте будет выброшено исключение. В этом поведение dynamic_cast отличается от static_cast.
Это было основано на моем понимании static_cast и dynamic_cast:
static_cast выполняется в compile-time и представляет просто добавление константы к значению указателя
dynamic_cast выполняется в run-time и нуждается в информации, которая связана с конкретным объектом на который указывает указатель.
На это мое замечание Егор и Dilmah ответили, что жизнь сложнее, с невалидными указателями работать надо осторожно (по стандарту копировать их и то опасно). С практической точки зрения было указано на опасность преобразования указателя на объект использующий виртуальный базовый класс. (Естественно не обсуждался вопрос что любое использование полученного значения указателя может привести к непредсказуемым последствиям — это и так понятно)
В процессе обсуждения было выяснено, что static_cast не может быть использован для преобразования от виртуального базового класса к указателю на наследник. Это просто не компилируется, так как для такого преобразования нужна run-time информация.
Но Егор указал пример обратного преобразования, который благополучно компилировался и оканчивался крахом программы. В упрощенном виде:
struct Base {};
struct D : public virtual Base {};
int main()
{
D* d = new D;
Base * b = static_cast<Base*>(d);
cout << b << endl;
d = NULL;
b = static_cast<Base*>(d);
cout << b << endl;
d = (D*)32;
cout << "d=" << d <<endl;
b = static_cast<Base*>(d); //здесь происходит крах программы
cout << b << endl;
}
Пример, когда использование static_cast невалидного указателя приводит краху, был найден. Но я задумался над словами Егора, который он сказал несколько раз — "здесь можно и без static_cast". Действительно, преобразования указателя к базовому типу является одним из основных преобразований в C++ и не требует использования никакого из явных операторов преобразования c++, ни тем более преобразования в стиле C. Т.е. static_cast в этом случае просто не требуется, а компилятор милостиво нам прощает эту неточность:
#include <iostream>
using namespace std;
struct Base {};
struct D : public virtual Base {};
int main()
{
D* d = new D;
Base * b = d;
cout << b << endl;
d = NULL;
b = d;
cout << b << endl;
d = (D*)32;
cout << "d=" << d <<endl;
b = d;
cout << b << endl;
}
Таким образом, я бы свое утверждения сформулировал так — если у нас есть указатель, и нам нужно преобразовать его к другому типу, и для этого необходимо использовать static_cast, то в практическом смысле это не приведет ни к каким исключениям или краху программы во время выполнения static_cast вне зависимости от валидности исходного казателя. Конечно, можно представить себе машинную архитектуру, которая контролирует указатели run-time, но я не слышал о таких современных архитектурах. Ну и конечно, речь идет именно о чистых указателях, а не о итераторах, пользовательских указателях-объектах и т.д. и т.п.)
Конечно вопрос довольно академический, но я могу представить что это можно использовать при поиске багов указателей при использовании системных функций типа IsBadWritePtr(), _CrtIsValidHeapPointer, _CrtIsValidPointer