c ++では、反復子などで*演算子をオーバーロードできますが、矢印(->)(。*)演算子は*演算子をオーバーロードするクラスでは機能しません。プリプロセッサは->のすべてのインスタンスを(* left).rightに簡単に置き換えることができ、それによりイテレータの実装が改善されると思います。->が異なる実用的な理由はありますか、それとも言語/デザイナーの特殊性ですか?
c ++では、反復子などで*演算子をオーバーロードできますが、矢印(->)(。*)演算子は*演算子をオーバーロードするクラスでは機能しません。プリプロセッサは->のすべてのインスタンスを(* left).rightに簡単に置き換えることができ、それによりイテレータの実装が改善されると思います。->が異なる実用的な理由はありますか、それとも言語/デザイナーの特殊性ですか?
回答:
foo->bar
等しいルール(*foo).bar
は、組み込み演算子にのみ適用されます。
単項にoperator *
は、常にポインター逆参照セマンティクスがありません。行列の転置、ゼロ以上のパーサー一致、またはほとんど何でも意味するライブラリを作成できます。
単項演算子をオーバーロードするものが、意味をなさないかもしれないセマンティクスでoperator *
、operator ->
あなたが要求しなかったものを突然得るなら、それは言語をより面倒にします。
operator ->
は個別にオーバーロード可能であるため、必要な場合は最小限の労力でオーバーロードできます。
また、そのようなオーバーロードにはoperator ->
、チェーン内の1 つが生のポインターを返すまで呼び出しを自動的にチェーンするなど、かなり興味深いプロパティがあることに注意してください。これは、スマートポインターやその他のプロキシタイプに非常に役立ちます。
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>
struct Foo
{
boost::shared_ptr<std::string> operator -> () const
{
return boost::make_shared<std::string>("trololo");
}
};
int main()
{
Foo foo;
std::cerr << foo->size() << std::endl;
}
->
演算子へのチェーンを使用して、何かへの生のポインターを取得し、そのメンバーを逆参照およびアクセスする方法を示しています。演算子->が連鎖しなかった場合、shared_ptrは生のポインタではないため、例の形式は正しくありません。
A->B
シンタックスチェーンを使用して、最大で1つの追加呼び出しを行います。C ++->バイナリ構文が実際に行うのは、オブジェクトをopeartor->
直接呼び出すことではありません-代わりに、オブジェクトの型をA
調べ、その生のポインタかどうかを確認します。それは、その後であれば->
、それをderefsて実行B
その上、それ以外の場合は、オブジェクトのを呼び出しoperator->
(いずれかの天然生ポインタまたは別のものを使用して結果をderefs、operator->
そして次に実行B
結果に
x->m
と解釈されることを示し(x.operator->())->m
ます。LHSが適切なオーバーロードをoperator->
再度持つものである場合、このプロセス(*x).m
は5.2.5 / 2の通常の効果が現れるまで繰り返されます。
「C ++プログラミング言語」は、これらの演算子が異なるように異なるという事実を説明していますが、次のようにも述べています。
あなたはこれらの演算子の複数を提供する場合、あることを保証するのが賢明であるのと同様に、等価性を提供するのが賢明かもしれない
++x
とx+=1
同じ効果を持つx=x+1
単純な変数のためのx
+ =、++ならば、いくつかのクラスの、=、および+が提供されます。
したがって、言語デザイナーは、常に同じであると仮定するのではなく、異なる方法でオーバーロードしたい場合があるため、別々のオーバーロードポイントを提供したようです。
一般的なルールとして、C ++は柔軟性を優先するように設計されているため、オーバーロード*
とオーバーロード->
は分離されています。そうすることはかなり珍しいことですが、ひどくしたい場合は、まったく異なることを行うためにそれらのオーバーロードを書くことができます(たとえば、C ++内に実装されたドメイン固有の言語に意味があるかもしれません)。
ただし、イテレーターはどちらの使用もサポートします。古代の実装で(*iter).whatever
はiter->whatever
、の代わりにを必要とするライブラリを見つけるかもしれませんが、そうであれば、それは言語の特性ではなく実装のバグです。すべての標準コンテナ/アルゴリズム/イテレータの実装に必要な作業量を考えると、一部の初期リリースがやや不完全であったことは驚くことではありませんが、そのように意図されたものではありませんでした。
(*i).m
有効なイテレータはすべてi->m
同じセマンティクスでサポートする必要があります。