基本クラスからprotected
に指定されたメンバーは、派生クラスのインスタンスからのみアクセスできることを知っています。これは標準の機能であり、スタックオーバーフローで何度も議論されています。
しかし、ユーザーchtz has me me のように、メンバーポインターを使用してこの制限を回避することは可能です。
struct Base { protected: int value; };
struct Derived : Base
{
void f(Base const& other)
{
//int n = other.value; // error: 'int Base::value' is protected within this context
int n = other.*(&Derived::value); // ok??? why?
(void) n;
}
};
なぜこれが可能ですか、それは実装のどこかで必要な機能またはグリッチまたは標準の文言ですか?
コメントから別の質問が出てきました: if Derived::f
は実際のBase
で呼び出されますが、未定義の動作ですか?
class member accessexpr.ref (_aclass.amember
_)を使用して、を使用してメンバーにアクセスできないという事実access control[class.access] は、他の式を使用してこのメンバーにアクセスできないようにしません。
式_&Derived::value
_ (タイプは_int Base::*
_) は完全に標準に準拠しており、value
のメンバーBase
を指定します。次に、式_a_base.*p
_ここで、p
はBase
のメンバーへのポインターであり、_a_base
_はBase
のインスタンスでもあります 標準準拠 =。
したがって、標準に準拠するコンパイラは、式other.*(&Derived::value);
を定義された動作にする必要があります。メンバvalue
of other
にアクセスします。
ハックですか?
reinterpret_cast
を使用するのと同様に、これは危険である可能性があり、バグを見つけるのが難しい可能性があります。しかし、それはうまく形成されており、それが機能するかどうかは間違いありません。
類推を明確にするために:reinterpret_cast
の動作も標準で正確に指定されており、UBなしで使用できます。しかし、reinterpret_cast
は型システムを迂回し、型システムは理由があります。同様に、メンバートリックへのこのポインターは標準に従って適切に形成されていますが、メンバーのカプセル化を回避し、そのカプセル化は(通常)理由のために存在します(プログラマーがカプセル化を軽率に使用できると思うので、通常言う)。
[それは]実装または標準の文言のどこかでグリッチですか?
いいえ、実装は正しいです。これが、言語が機能するように指定された方法です。
Derived
のメンバー関数は、ベースの保護されたメンバーであるため、明らかに&Derived::value
にアクセスできます。
その操作の結果は、Base
のメンバーへのポインターです。これは、Base
への参照に適用できます。メンバーのアクセス権限は、メンバーへのポインターには適用されません。メンバーの名前にのみ適用されます。
コメントから別の質問が浮上しました:Derived :: fが実際のBaseで呼び出された場合、それは未定義の動作ですか?
UBではありません。 Base
にはメンバーがいます。