Подтвердить что ты не робот

Условный оператор + upcast + константа

Вдохновленный этот вопрос, я пробовал следующий код:

struct A {
  virtual void doit() const = 0;
};

struct B : public A {
  virtual void doit() const;
};

struct C : public A {
  virtual void doit() const;
};

void
foo(bool p)
{
  const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C()));
  a.doit();
}

Каждый компилятор, который я пробовал, принимает этот код с -Wall -Werror и генерирует сборку, которую я хочу. Но после тщательного изучения раздела 12.2 ( "Временные" ) раздела С++ 03 и раздела 5.12 ( "Условный оператор" ) я не уверен, что это гарантировано.

Итак, этот допустимый код, или он вызывает поведение undefined? Отличается ли ответ для С++ 03 и С++ 11?

Приветствуются цитаты из соответствующих спецификаций.

4b9b3361

Ответ 1

О, это очень недействительно.

Рассмотрим:

#include <iostream>
using namespace std;

struct A {
    virtual ~A() { cout << "~A" << endl; }
    virtual void doit() const = 0;
};

struct B : public A
{
    ~B() override { cout << "~B" << endl; }
    void doit() const override { cout << "A::doit" << endl; }
};

struct C : public A
{
    ~C() override { cout << "~C" << endl; }
    virtual void doit() const { cout << "C::doit" << endl; }
};

void foo(bool p)
{
    cout << "foo( " << p << ")" << endl;
    const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C()));
    a.doit();
}

auto main( int argc, char* argv[] ) -> int
{
    cout << boolalpha;

    foo( true );
    cout << endl;
    foo( false );
}

Вывод в Coliru Viewer, используя g++ 4.8:

foo( true)

~B

~A

pure virtual method called

terminate called without an active exception

bash: line 7: 16922 Aborted                 (core dumped) ./a.out

Это UB, поэтому любое объяснение может быть правдой, но можно быть достаточно уверенным, не глядя на сборку, что происходит:

  • Создается временная структура.
  • Он привязан к ссылке.
    Это ссылка привязана к ссылке, поэтому не требуется создание нового временного или среза.
  • Временное уничтожается.
  • В качестве части этого динамический тип (указатель vtable) изменяется на A, который является абстрактным.
  • Вызывается чистый виртуальный объект в A.