メンバーアクセス演算子->
、.*
、->*
などを除く、ほとんどの演算子のオーバーロードを理解しています。
特に、これらの演算子関数に何が渡され、何が返されますか?
演算子関数(例:operator->(...)
)は、どのメンバーが参照されているかをどのように知るのですか?わかりますか?知る必要さえありますか?
最後に、考慮に入れる必要のあるconstの考慮事項はありますか?たとえば、operator[]
のようなものをオーバーロードする場合、通常はconstバージョンと非constバージョンの両方が必要になります。メンバーアクセス演算子には、constバージョンと非constバージョンが必要ですか?
->
これが唯一の本当にトリッキーなものです。非静的メンバー関数である必要があり、引数を取りません。戻り値は、メンバー検索を実行するために使用されます。
戻り値がポインタではなく、クラス型の別のオブジェクトである場合、後続のメンバー検索もoperator->
関数によって処理されます。これは「ドリルダウン動作」と呼ばれます。言語は、最後の呼び出しがポインターを返すまで、operator->
呼び出しを連結します。
struct client
{ int a; };
struct proxy {
client *target;
client *operator->() const
{ return target; }
};
struct proxy2 {
proxy *target;
proxy &operator->() const
{ return * target; }
};
void f() {
client x = { 3 };
proxy y = { & x };
proxy2 z = { & y };
std::cout << x.a << y->a << z->a; // print "333"
}
->*
これは特別なことは何もないという点で注意が必要です。 non-overloadedバージョンでは、左側にクラス型へのポインターのオブジェクト、右側にメンバー型へのポインターのオブジェクトが必要です。しかし、それをオーバーロードすると、好きな引数を取り、必要なものを返すことができます。非静的メンバーである必要さえありません。
言い換えると、これは+
、-
、および/
のような単なる通常の二項演算子です。参照: 無料の演算子-> *は悪をオーバーロードしますか?
.*
および.
これらはオーバーロードできません。左側がクラス型の場合、すでに組み込みの意味があります。おそらく、左側のポインター用にそれらを定義できるのは少し理にかなっているかもしれませんが、言語設計委員会は、有用ではなく混乱を招くと判断しました。
->
、->*
、.
、および.*
のオーバーロードは、式が定義されていない場合にのみ入力でき、有効な式の意味を変更することはできません。オーバーロードなし。
演算子->は特別です。
「追加の非定型の制約があります。ポインター逆参照演算子も持つオブジェクト(またはオブジェクトへの参照)を返すか、ポインター逆参照演算子の矢印が指すものを選択するために使用できるポインターを返す必要があります。 」 ブルース・エッケル:Thinking CPP Vol-one:operator->
余分な機能は便宜上提供されているため、呼び出す必要はありません。
a->->func();
簡単にできます:
a->func();
これにより、演算子->他の演算子のオーバーロードとは異なります。
メンバーアクセス.
をオーバーロードすることはできません(つまり、->
が行うことの2番目の部分)。ただし、単項式dereferencing operator *
(つまり、->
が行うことの最初の部分)をオーバーロードできます。
C++の->
演算子は、基本的に2つのステップの結合であり、x->y
が(*x).y
と同等であると考える場合、これは明らかです。 C++では、x
がクラスのインスタンスである場合、(*x)
パーツの処理をカスタマイズできます。
->
オーバーロードのセマンティックは、C++で通常のポインター(ポイントされたオブジェクトを見つけるために使用される)を返すか、このクラスが->
演算子も提供する場合に別のクラスのインスタンスを返すことができるため、やや奇妙です。この2番目のケースでは、この新しいインスタンスから間接参照オブジェクトの検索が続行されます。
->
演算子は、どのメンバーがポイントされているかを知らず、実際のメンバーアクセスを実行するオブジェクトを提供するだけです。
さらに、constバージョンとnon-constバージョンを提供できない理由はありません。
Operator->()をオーバーロードすると(ここでは引数は渡されません)、コンパイラーが実際に行うのは、型への実際のポインターを返すまで再帰的に呼び出します。次に、正しいメンバー/メソッドを使用します。
これは、たとえば、実際のポインターをカプセル化するスマートポインタークラスを作成する場合に便利です。オーバーロードされたoperator->が呼び出され、それが行うこと(たとえば、スレッドセーフのためのロック)が行われ、内部ポインターが返された後、コンパイラーはこの内部ポインターに対して->を呼び出します。
Constnessに関しては、コメントおよびその他の回答で回答されています(両方を提供することができ、また提供する必要があります)。