web-dev-qa-db-ja.com

C ++でアップキャストが違法になるのはいつですか?

特にC++では、アップキャストとダウンキャストの一般的な違いを理解していると思います。基本クラスポインターを派生クラスポインターにキャストすると、ポイントされている基本クラスオブジェクトが派生クラスのすべてのメンバーを持っていると想定されるため、常にダウンキャストできるとは限りません。

学期の初めに、私の教授はC++でアップキャストすることも時々違法であるとクラスに話しました、しかし私は私のノートで理由を逃したようで、これがいつ起こったのか思い出せません。

C++でアップキャストするのはいつ違法ですか?

56
Connor Foley

「不正」とは、形式が正しくないことを意味し、基本クラスにアクセスできない、またはあいまいであることは違法です。

  • たとえば、基本クラスがプライベートの場合はアクセスできません。

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

    C++ 11でも、Cスタイルのキャストはアクセス保護を「突破」し、正式に正しいアップキャストを実行できることに注意してください。

    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
    

    このような場合、アップキャストは、中間のアップキャストを使用して目的のアップキャストパスを明示的に「ウォーク」して、ベースが曖昧でなくなるポイントまで実行できます。

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

基本クラスがあいまいな場合(異なるパスを介して2回以上継承される場合)、単一のステップでアップキャストを行うことはできません。

基本クラスにアクセスできない場合、アップキャストする唯一の方法は、Cスタイルのキャストを使用することです。これはそのキャストの特別なケースであり、それは仕事をすることができる唯一のものです。基本的に、アクセシビリティによって制限されないstatic_castとして動作します。


標準。

C++ 11§5.4/ 4:

…[A Cキャスト]では、次の状況でstatic_castを実行すると、基本クラスにアクセスできない場合でも変換は有効です。

  • 派生クラス型のオブジェクトへのポインタ、または派生クラス型の左辺値または右辺値は、それぞれ、明確な基本クラス型へのポインタまたは参照に明示的に変換できます。
  • 派生クラス型のメンバーへのポインターは、明確な非仮想基本クラス型のメンバーへのポインターに明示的に変換できます。
  • 明確な非仮想基本クラス型のオブジェクトへのポインター、明確な非仮想基本クラス型の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;
};
17

C++でアップキャストが不適切な形式である(コンパイル時に診断される)2つのケースがあります。

  1. 問題の基本クラスはアクセスできません

    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;
    }
    
  2. 問題の基本クラスサブオブジェクトはあいまいではない

    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;
    }
    
10
Deduplicator