Почему reinterpret_cast работает в частном наследовании

Я читал о спецификаторах доступа при применении наследования и знаю, что в private inheritance мы не могли преобразовать производный класс в базовый, используя указатели/ссылки.

Но когда я использовал reinterpret_cast, это сработало. ниже мой тестовый код:

class base {
int _a;
public: 
    base(int a): _a(a) {}
    base(): _a(0) {}
};

class derived : private base
{
public:
    derived(int b):base(b) {};  
};

int main() {

    derived b(25); 
    base &a = static_cast<base&>(b);//this line will generate a compile error
    base &c = reinterpret_cast<base&>(b);  //here it works 
}

Итак, мой вопрос даже в частном наследовании, почему базовый класс будет выставлен с использованием retinterpret_cast?

Благодарю вас!

//EDIT 2

class base {
    int _a; 
public:         
    base(int a): _a(a) {}
    base(): _a(100) {}  
    ~base() { std::cout << "deleting base" << _a << "\n"; }
};

class derived : private base
{
public:
    virtual ~derived() = default;
    derived(int b):base(b) {};
};

int main() {

    derived b(25); 
    base &c = reinterpret_cast<base&>(b); 
}

//OutPut : Deleting 25

person Blood-HaZaRd    schedule 03.07.2018    source источник
comment
Вы не должны использовать static_cast или reinterpret_cast для повышения или понижения. Вместо этого используйте dynamic_cast   -  person Asesh    schedule 03.07.2018
comment
да, вы правы, я должен использовать dynamic_cst, для которого нужен полиморфный класс, но это было для теста :)   -  person Blood-HaZaRd    schedule 03.07.2018
comment
reinterpret_cast очень похож на старые приведения в стиле C, поскольку он в основном сообщает компилятору, что я знаю, что делаю, не беспокойте меня. Если вы на самом деле не знаете, что делаете, или допустили какие-либо ошибки, компилятор все равно не будет вас беспокоить и с радостью предоставит вам пистолет, чтобы вы могли выстрелить себе в ногу.   -  person Some programmer dude    schedule 03.07.2018
comment
На практике и в случае простого наследования оба static_cast, приведения в стиле C, reinterpret_cast компилируются как неоперативные (то есть код не генерируется, как для функции идентификации). Однако dynamic_cast имеет стоимость во время выполнения (и требует некоторого сгенерированного кода)   -  person Basile Starynkevitch    schedule 03.07.2018
comment
@Someprogrammerdude: известно о зле, которое живет внутри reinterpret_cast, но нарушает ли это частное наследование?   -  person Blood-HaZaRd    schedule 03.07.2018
comment
Итак, мой вопрос даже в частном наследовании, почему базовый класс будет выставлен с помощью retinterpret_cast - это не так. reinterpret_cast по-прежнему компилируется, даже если derived не наследуется от base совсем.   -  person davmac    schedule 03.07.2018


Ответы (2)


Нарушается ли частное наследование? Не совсем.

Доступность в C++ влияет только на то, в каких областях можно использовать идентификатор для правильной ссылки на что-либо. Система предназначена для защиты от Мёрфи, а не от макиавеллиевского трюка, который вы используете.

reinterpret_cast в основном вы говорите компилятору "забудь то, что ты знаешь, вместо этого доверься моему мнению". Так оно и есть. Вы утверждаете, что это lvalue на самом деле относится к base? Хорошо, будь по-твоему. Но компилятор ничего не сделает, чтобы защитить вас, он предполагает, что вы знаете, что делаете. Он может довольно легко сломаться. Вот пример @Dani, и вот этот:

class derived : private base
{
public:
    virtual ~derived() = default;
    derived(int b):base(b) {};  
};

Как вы думаете, что произойдет, если вы попытаетесь использовать c и вызвать функцию-член, использующую _a? Что он найдет вместо этого?

person StoryTeller - Unslander Monica    schedule 03.07.2018
comment
Класс C публично происходит от A и B (но эти последние выведены из базы, если я понял пример), поэтому C не сможет вызвать _a. Поправьте меня, если я ошибаюсь. - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - Какой класс C? c в вашем примере является ссылкой на базу. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Я думал, вы указали на пример Дани, в котором он использовал c. Итак, если я попытаюсь вызвать членский fct, используя _a, пусть говорят, что fct определен в базе классов, он показывает значение _a - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - я намеренно добавил виртуальную функцию. Он не видит _a coliru.stacked-crooked.com/a/dca89f30018ea13c - person StoryTeller - Unslander Monica; 03.07.2018
comment
@StoryTeller- Извини, что не понял тебя. Не могли бы вы объяснить немного подробнее, пожалуйста. производный класс не мог видеть _a, так как наследование является частным!?! - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - Нет. У вас есть производный объект. Вы лжете компиляторам, что на самом деле это base (и это не так). Компилятор ловит вас на слове. Он не вносит никакой корректировки, чтобы на самом деле получить базовую ссылку (как это было бы для общедоступной базы), поэтому вы получаете неопределенное поведение. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Пожалуйста, не могли бы вы попробовать взглянуть на EDIT2, возможно, я не понял код, на который вы указываете, но с помощью Edit 2 кажется, что _a хорошо напечатан. Может быть, если я добавлю переменную мембера в производный класс строки типа, например, тогда _a будет мусор! это то, на что ты намекаешь? - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - ваше редактирование не использует ссылку c для доступа к объекту ... Вывод, который вы видите, относится к обычному потоку программы, когда объект derived уничтожается, также вызываются конструкторы базового класса. Извините, но на данный момент я думаю, вам нужно вернуться к основам. . - person StoryTeller - Unslander Monica; 03.07.2018
comment
неопределенное поведение связано с тем, как правильно управлять указателем vtable (смещением)? - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - Полуформально, да. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Ах, какие ключевые слова могут помочь найти полный формальный ответ. Я не смогу уснуть, если не найду ответ :) - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd. Полный формальный ответ заключается в том, что это неопределенное поведение. Полуформальная часть заключается в том, что это не работает, потому что компиляторы помещают вещи перед членами. Но причина, по которой компиляторы могут помещать туда что-то, заключается в том, что стандарт говорит, что это неопределенное поведение. Хорошо сформированная программа на C++ не имеет неопределенного поведения. Авторы компиляторов используют это, чтобы делать вещи, потому что хорошая программа не будет делать ничего плохого. Звучит кругообразно, я знаю. Но это не так, когда вникаешь в то, что формально означает неопределенное поведение. Я думаю, вы можете исследовать поведение Undefined в целом :) - person StoryTeller - Unslander Monica; 03.07.2018
comment
@Blood-HaZaRd. Если вы хотите узнать, как много могут сделать разработчики компиляторов на основе неопределенного поведения, взгляните на эту программу coliru.stacked-crooked.com/a/1ac4fc3ea6a61b85 — Странно, что он звонит print, верно? Если вы раскомментируете static, произойдет сбой. Причина объясняется тем, что компиляторы позволяют себе делать, исходя из неопределенного поведения. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Хорошо, так что все это связано с внутренними действиями компилятора, которые он делает. И именно поэтому переинтерпретация — это зло, даже если бы мы могли сами вычислить коррекцию смещения указателя. Спасибо. - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - Почти. Жизнь (и C++), конечно, сложнее. Иногда reinterpret_cast можно заставить работать. Дело в том, что когда вы reinterpret_cast, это говорит компилятору, что вы точно знаете, что происходит. Так что лучше быть осторожным. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Я даже вижу в каком-то коде что-то вроде reinterpret_cast<std::uintptr_t>(static_cast<const base*>(&b) ) смеси статики и реинтерпретации, но здесь мы на 100% уверены в том, что мы манипулируем, когда статика извлекает хорошую вещь. - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd - Да, это одно из тех мест, где требуются более глубокие знания. Статическое приведение запрашивает у компилятора правильную настройку указателя, а reinterpret_cast затем берет этот возвращенный указатель и делает из него целое число. Результат сильно зависит от реализации. - person StoryTeller - Unslander Monica; 03.07.2018

reinterpret_cast не совпадает с static_cast
Рассмотрим следующий пример:

class A { int a; }
class B { int b; }
class C : public A, B { }

Приведение C к B с использованием static_cast изменит указатель на правильный результат, в то время как reinterpret_cast сохранит указатель таким же, что неверно.

person Dani    schedule 03.07.2018
comment
static_cast не будет работать за рамками C здесь. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Важно, если вы утверждаете что-то, чтобы быть правдой. Приведение C к B с использованием static_cast изменит указатель на правильный результат. Не выходит за рамки C. Это важно, потому что кто-то в будущем, скорее всего, попробует его, но он не скомпилируется. static_cast учитывает модификаторы доступа. И B все еще частный, кстати. - person StoryTeller - Unslander Monica; 03.07.2018
comment
Я не пытаюсь сказать, что весь ваш аргумент неверен, MI действительно является примером того, когда он может эффектно потерпеть неудачу. - person StoryTeller - Unslander Monica; 03.07.2018
comment
@Dani: моя цель - узнать, почему, когда я использую reinterpret_cast для частного наследования, он может получить ссылку на базовый класс? извините, если я не увидел подсказку в вашем ответе. - person Blood-HaZaRd; 03.07.2018
comment
@Blood-HaZaRd: это поведение undefined и в обычных компиляторах оно работает в некоторых условиях. При множественном наследовании, как в моем примере, это не сработает. В вашем случае это, вероятно, сработало, поскольку базовый класс занимает смещение 0 в классе. - person Dani; 04.07.2018
comment
@Dani: да, ты указал правильно. Управление смещением с помощью myOwn - это полное зло !! - person Blood-HaZaRd; 04.07.2018