Re[11]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: dilmah США  
Дата: 14.02.13 05:45
Оценка:
S>Если есть такой пример неприятностей во время выполнения static_cast, то интересно посмотреть.

неприятности именно во время выполнения.
При виртуальном наследовании для того чтобы сделать static_cast из базового типа в производный во всех разумных реализациях происходит обращение к памяти объекта (потому что смещение нельзя сделать константным как при невиртуальном наследовании, поэтому недостаточно просто прибавить/отнять константу). То есть во время выполнения static_cast будет происходить обращение к памяти рядом с указателем, что для плохого указателя чревато.
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 06:08
Оценка: 8 (1)
Здравствуйте, dilmah, Вы писали:


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]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 07:28
Оценка: 4 (1)
Здравствуйте, Serg27, Вы писали:

S>Этот фрагмент не компилируется ( error: cannot convert from base ‘Base’ to derived type ‘Derived’ via virtual base ‘Base’) здесь. Или имелось в виду какое-то другое преобразование?


Да, обратное. Его можно и static_cast'ом сделать, если охота...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 08:25
Оценка:
Здравствуйте, Serg27, Вы писали:

S>Мне действительно интересно — можно ли придумать ситуацию, когда static_cast указателя приведет к выбросу исключения. Вопрос о том что будет потом при использовании полученного указателя давайте обсуждать не будем — тут все понятно. Ну и такой должен сработать на современных распространенных архитектурах.


Собственно вот...
Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
На MSVC 8 всё так, как тебе хотелось -- GP внутри static_cast...

Только он там не обязвтельный, даже если его убрать и оставить неявное приведение типов, всё равно жахнет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 08:31
Оценка:
Здравствуйте, 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]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Serg27  
Дата: 14.02.13 08:44
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

E
E>Собственно вот...
E>Я так понимаю, что "компилируется" в ответ на "запустить" обозначает, что до return в main мы не добрались
это означает, что загрузка сервера велика, и он все еще компилирует. У меня когда я зашел статус был — "успешно"
Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь

Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Автор: Serg27
Дата: 14.02.13

Все таки мы его завалили
Re[15]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 14.02.13 10:33
Оценка:
Здравствуйте, Serg27, Вы писали:

E>>Собственно вот...

S>Т.е. ничего не падает. Вот мой вариант с печатью в конце — здесь

S>Я ниже уже дал вариант, когда происходит падение на static_cast — здесь
Автор: Serg27
Дата: 14.02.13

S>Все таки мы его завалили

Ну просто эта ideone зачем-то оптимизирует и всё выкинула.

Надо вот так просто сделать было и всё...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[12]: Можно ли определить, является ли тип наследником данного, на шаблонах?
От: Erop Россия  
Дата: 15.02.13 05:06
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Рукопашное решение:

J>
J>if (typeid(x) == typeid(T1)) {....}
J>else if (typeid(x) == typeid(T1)) {....}
J>...
J>

J>Визитёр:
J>
J>struct Visitor {
J>  void visit(T1& x) {....}
J>  void visit(T2& x) {....}
J>  ...
J>};
J>


Может быть и так:
if( IIinterface1* i1 = x->QI<IIinterface1*>() ) {
    i1->useInterfce1();
}
if( IIinterface2* i2 = x->QI<IIinterface2*>() ) {
    i1->useInterfce2();
}


J>И то, и другое придется переписывать, если, как ты там сказал, "понадобится добавить в иерархию ещё пяток классов, а из уже имеющихся 150, примерно 40 переписать".


Тогда переписывать может и не понадобиться...

Фишка же не в том, что бы формально не использовать визитёра, и как-то это дело закомуфлировать, а в том, что бы так спроектировать программу, что визитёр будет не нужен...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Безопасность static_cast (резюме обсуждения)
От: Serg27  
Дата: 17.02.13 10:21
Оценка:
.
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
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.