Приведение ссылки на базовый класс: стандартное поведение? - PullRequest
0 голосов
/ 10 мая 2019

У меня есть ситуация, подобная следующей:

class Getter
 {public: virtual void doSomething {std::cout<<"Nothing";};
  };
class Getter_A: public Getter
 {public: void doSomething {std::cout<<"A";};
  };
class Getter_B: public Getter
 {public: void doSomething {std::cout<<"B";};
  };

Затем у меня есть блок кода, в котором я хочу использовать одну из этих вещей Getter.Таким образом, на английском у меня было бы следующее:

{Getter & getter=wantA?Getter_A():Getter_B;
 getter.doSomething();
 };

Конечно, в C ++ это не совсем работает, потому что троичный оператор не достаточно умен, чтобы искать общий базовый класс, поэтому я делаюэто:

{Getter & getter=wantA?(Getter &)Getter_A():(Getter &)Getter_B;
 getter.doSomething();
 };

Это элегантно и читабельно.Вопрос в том, работает ли он в C ++.Поскольку компиляторов C ++ нет, и поскольку я не могу найти однозначного ответа в справочнике, я спрашиваю здесь.

Общее основание: «Компиляторы языка, похожего на C ++» Iпопытался сделать следующее:

  1. Создать объект Getter_A или Getter_B.
  2. Позвоните doSomething.
  3. Удалите созданный объект.

Открытый вопрос: что делать для вызова?

Clang на Mac вызывает Getter_A::doSomething() или Getter_B::doSomething().Таким образом, за кадром эта ссылка Getter& является ссылкой либо на объект Getter_A, либо на объект Getter_B.

Visual C ++ 6.0 вызывает Getter::doSomething().Таким образом, за кадром он удаляет Getter_A сущность из созданного объекта и вместо этого делает ссылку на голый объект Getter.Это становится более впечатляющим, если Getter::doSomething сделано чистым, а не иметь тело.

Теперь Visual C ++ 6.0 славится не компиляцией C ++, а совершенно другим языком с тем же именем.Тем не менее, было бы хорошо узнать, ожидает ли C ++ (в отличие от идеи того или иного автора о том, что такое C ++ автор) компилятора, что ссылка на базовый класс по-прежнему будет ссылкой на исходный объект во всехего великолепная виртуальность, или то, правильно ли Microsoft в этом случае раздеть объект обнаженным при приведении ссылки.

Обход, который я придумал и протестировал, это:

{Getter_A a; Getter_B b;
 Getter &getter=*(wantA?(Getter *)&a:&b);
 getter.doSomething();
 }

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

1 Ответ

3 голосов
/ 10 мая 2019

Я предполагаю, что ваш "элегантный и читабельный" подход содержит опечатку, и вы намереваетесь прочитать

{Getter & getter=wantA?(Getter &)Getter_A():(Getter &)Getter_B();
getter.doSomething();
};

(обратите внимание на () после Getter_B, которые отсутствуют в вашем описании).

В любом случае это дает неопределенное поведение, так как выбранный объект перестает существовать до оператора getter.doSomething(). Таким образом, тестирование, которое называется doSomething(), является академическим - даже если два компилятора дают разные результаты, они оба верны.

Что вам нужно сделать, так это убедиться, что используемый вами объект продолжает существовать до вызова.

Существуют различные способы, такие как (по сути) ваш «обходной путь».

 Getter_A a;
 Getter_B b;

 Getter &getter = wantA ? (Getter &) a : (Getter &) b;
 getter.doSomething();

или (более явно)

 Getter_A a;
 Getter_B b

 Getter &ra(a), &rb(b);
 Getter &getter = wantA ? ra : rb;
 getter.doSomething();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...