Когда в С++ происходит анонимность?

Я уверен, что понимаю общую разницу между повышением и понижением, особенно на С++. Я понимаю, что мы не всегда можем опуститься, потому что приведение указателя базового класса к указателю производного класса предполагает, что объект базового класса, на который указывают, имеет все члены производного класса.

В начале семестра, мой профессор сказал классу, что иногда также запрещено подниматься на С++, но, похоже, я пропустил причину, почему в моих заметках, и я не могу вспомнить, когда это происходит.

Когда это незаконно для повышения на С++?

Ответ 1

Если под "незаконным" подразумевается плохо сформированный, то он является незаконным, если базовый класс недоступен или неоднозначен.

  • Это недоступно, если, например, базовый класс является закрытым.

    class A {};
    class B : A {};
    ...
    B b;
    A *pa = &b; // ERROR: base class is inaccessible
    

    Обратите внимание, что даже в С++ 11 приведение в стиле C может "прорвать" защиту доступа и выполнить формально корректный upcast

    A *pa = (A *) &b; // OK, not a `reinterpret_cast`, but a valid upcast
    

    Конечно, этого следует избегать.

  • Это неоднозначно, если ваш тип источника содержит несколько базовых подобъектов целевого типа (через множественное наследование).

    class A {};
    class B : public A {};
    class C : public A {};
    class D : public B, public C {};
    
    D d;
    A *pa = &d; // ERROR: base class is ambiguous
    

    В таких случаях upcast может выполняться путем явного "хождения" желаемого пути upcast с промежуточным повышением до точки, где база больше не является двусмысленной.

    B* pb = &d;
    A* pa = pb; // OK: points to 'D::B::A' subobject
    

Ответ 2

Если базовый класс неоднозначен (унаследован два или более раз по разным путям), вы можете сделать t up-up за один шаг.

Если базовый класс недоступен, то единственным способом ускорения является использование стилей стиля C. Это особый случай этого актера, он единственный, кто может справиться с этой задачей. По существу он тогда ведет себя как static_cast, который не ограничен доступностью.


Standardese.

С++ 11 §5.4/4:

" & hellip; в [a C cast] выполняется static_cast в следующих ситуациях: преобразование действительно, даже если базовый класс недоступен:

  • указатель на объект типа производного класса или lvalue или rvalue типа производного класса может быть явно преобразован в указатель или ссылку на однозначный тип базового класса, соответственно;
  • указатель на член типа производного класса может быть явно преобразован в указатель на член недвусмысленный тип не виртуального базового класса;
  • указатель на объект недвусмысленного типа не виртуального базового класса, glvalue однозначного тип не виртуального базового класса или указатель на член однозначного типа не виртуального базового класса могут быть явно преобразованы в указатель, ссылку или указатель на член типа производного класса, соответственно.

Пример двусмысленности:

struct Base {};
struct M1: Base {};
struct M2: Base {};
struct Derived: M1, M2 {};

auto main() -> int
{
    Derived d;
    //static_cast<Base&>( d );                      //! Ambiguous
    static_cast<Base&>( static_cast<M2&>( d ) );    // OK
}

Пример недоступной базы, с (обычно) настройкой адреса в броске:

struct Base { int value; Base( int x ): value( x ) {} };

class Derived
    : private Base
{
public:
    virtual ~Derived() {}       // Just to involve an address adjustment.
    Derived(): Base( 42 ) {}
};

#include <iostream>
using namespace std;

auto main() -> int
{
    Derived d;
    Base& b = (Base&) d;
    cout << "Derived at " << &d << ", base at " << &b << endl;
    cout << b.value << endl;
};

Ответ 3

Есть два случая, когда восходящее событие плохо сформировано в С++ (диагностировано во время компиляции):

  • Основной класс, о котором идет речь, недоступен:

    class base {};
    class derived : base {};
    
    int main() {
        derived x;
        base& y = x; // invalid because not accessible.
        // Solution: C-style cast (as static_cast without access-check)
        base& y1 = (base&)x;
    }
    
  • Подпоследовательный объект базового класса не однозначно:

    class base {};
    struct A1 : base {};
    struct A2 : base {};
    
    struct derived : A1, A2 {};
    int main() {
        derived x;
        base& y = x; // invalid because ambiguous.
        // Solution 1, scope resolution:
        base& y1 = static_cast<A1::base&>(x);
        base& y2 = static_cast<A2::base&>(x);
        // Solution 2, intermediate unambiguous steps:
        A1& a1 = x;
        A2& a2 = x;
        base& ya1 = a1;
        base& ya2 = a2;
    }