46

C++ 構文では、次のように構造体/クラス内でオーバーロードされた演算子を定義できます。

struct X
{
   void operator+(X);
}

または次のような構造体/クラスの外:

void operator+(X, X);

ただし、次のようにはなりません:

struct X
{
   static void operator+(X, X);
}

この決定の理由を知っている人はいますか?3 番目の形式が許可されないのはなぜですか? (MSVC では構文エラーが発生します。) この背後に何らかのストーリーがあるのではないでしょうか?

PS最初と2番目の定義が同時に存在すると、あいまいさが生じます。

1>CppTest1.cxx
1>c:\ballerup\misc\cf_html\cpptest1.cxx(39) : error C2593: 'operator +' is ambiguous
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(13): could be 'void B1::operator +(B1 &)'
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(16): or       'void operator +(B1 &,B1 &)'
1>        while trying to match the argument list '(B1, B1)'

このあいまいさが 1,3 または 2,3 の間よりも優れている理由がわかりません。

4

9 に答える 9

18

私は、この概念に関する C++ の議論について具体的な知識を持っていないので、無視してかまいません。

しかし、私にとっては、質問が逆になっています。問題は、「なぜこの構文が許可されるのか?」ということです。

現在の構文に勝る利点はまったくありません。非静的メンバー関数バージョンには、提案された静的バージョンと同じプライベート メンバーへのアクセス権があります。そのため、プライベートにアクセスして実装する必要がある場合は、クラスのほとんどのメンバーで一般的に行うのとまったく同じように、それを非静的メンバーにします。

非対称演算子 (つまり: ) の実装が容易になるわけではありませんoperator+(const X &x, const Y &y)。これを実装するためにプライベート アクセスが必要な場合でも、いずれかのクラスでフレンド宣言が必要です。

だから、それが存在しない理由は、それが必要ないからだと思います。非メンバー関数と非静的メンバーの間で、必要なすべてのユース ケースがカバーされています。


または、別の言い方をすれば:

無料関数は、静的関数システムでできることすべて、およびそれ以上のことができます。

無料の関数を使用することで、テンプレートで使用される演算子に対して発生する引数依存のルックアップを取得できます。静的関数は特定のクラスのメンバーでなければならないため、これを行うことはできません。また、クラスの外部からクラスに追加することはできませんが、名前空間に追加することはできます。したがって、ADL コードを機能させるために特定の名前空間に演算子を配置する必要がある場合は、それが可能です。静的関数演算子ではそれを行うことはできません。

したがって、フリー関数は、提案された静的関数システムが提供するすべてのスーパーセットです。許可するメリットがないので、許可する理由がなく、許可されません。


ファンクターをインスタンス化せずに使用できるようにするのはどれですか?

それは言葉の矛盾です。「ファンクター」は「関数オブジェクト」です。型はオブジェクトではありません。したがって、ファンクターにはなりません。インスタンス化されたときにファンクタになる型にすることができます。しかし、型だけではファンクターにはなりません。

さらに、Typename::operator()staticを宣言できるということは、それTypename()があなたが望むことをするという意味ではありません。その構文にはすでに実際の意味があります。つまりTypename、デフォルトのコンストラクターを呼び出して一時オブジェクトをインスタンス化します。

最後に、すべてがそうでなかったとしても、それが実際に何の役に立つのでしょうか? 何らかの型の callable を取るほとんどのテンプレート関数は、ファンクターと同様に関数ポインターでも機能します。単にファンクターだけでなく、内部データを持つことができないファンクターにインターフェースを制限したいのはなぜですか? つまり、キャプチャ ラムダなどを渡すことができないということです。

状態を含むことができないファンクターは何の役に立つでしょうか? 状態を持たない「ファンクター」をユーザーに渡すように強制したいのはなぜですか? また、ユーザーがラムダを使用できないようにしたいのはなぜですか?

したがって、あなたの質問は誤った仮定から導き出されたものです。たとえそれがあったとしても、あなたが望むものは得られないでしょう。

于 2012-08-10T01:28:17.073 に答える
18

そのような演算子を呼び出す明確な構文がないため、何かを作成する必要があります。次の変数を考慮してください。

X x1;
X x2;

ここで、演算子の代わりに通常のメンバー関数を使用しているふりをしましょう。例で変更operator+したとしましょうplus

3 つの呼び出しはそれぞれ次のようになります。

x1.plus(x2);
plus(x1, x2);
X::plus(x1, x2);

を使用して演算子呼び出しを行う場合+、コンパイラはどのようにして演算子をスコープ内で検索することを認識しますXか? 通常の静的メンバー関数に対してはそれを行うことはできず、演算子にはあいまいさを解消するための特別な権限が与えられていません。

ここで、プログラムで 2 番目と 3 番目の形式の両方が宣言されているかどうかを検討してください。x1 + x2コンパイラが常に無料の関数を選択する必要があると言った場合、呼び出しがあいまいになります。x1 X::+ x2唯一の本当の選択肢は、見た目が醜いようなものです。以上のことから、標準化委員会は静的メンバ バージョンを単純に禁止することを決定したに違いありません。静的メンバ バージョンが達成できることはすべて、代わりにフレンド フリー関数を使用して実行できるからです。

于 2012-08-10T02:36:25.053 に答える
2

静的メンバー関数は、クラスを支援するユーティリティに使用できますが、何らかの理由でメンバーではありません。静的クラス メンバー関数として表現されるユーティリティの中で、演算子があると便利な場合があることは容易に想像できます。

もちろん、一部のオーバーロードされた演算子がクラス C を主引数として受け取る場合、それをクラス C の静的メンバーにする正当な理由はありません。それは単に非静的メンバーになる可能性があるため、その引数を暗黙的に取得します。

ただし、クラス C の静的メンバーは、C 以外のクラスでオーバーロードされた演算子である可能性があります。

ファイル スコープ が存在するとしますoperator ==(const widget &, const widget &);。私のsquiggleクラスでは、widgetオブジェクトを扱っていますが、別の比較が必要です。

自分用に作ればいいのにstatic squiggle::operator == (const widget &, const widget &);

クラススコープから、これは簡単に呼び出すことができます:

void squiggle::memb(widget a, widget b)
{
   if (a == b) { ... } // calls static == operator for widgets
}

クラス スコープの外からは、明示的なスコープ解決と明示的な演算子呼び出し構文を組み合わせてのみ呼び出すことができます。

void nonmemb(widget a, widget b)
{
   a == b;  // calls the widget member function or perhaps nonstatic operator
   squiggle::operator ==(a, b); // calls squiggle class' utility
}

これは悪い考えではありません。さらに、演算子ではなく、通常のオーバーロードされた関数でそれを行うことができます。ウィジェットの比較がcompare関数で行われる場合、非メンバーcompareまたはwidget::compareが存在する可能性があり、squiggle::compareを取るが存在する可能性がありますwidgets

したがって、C++ でサポートされていない唯一の側面は、演算子を使用した構文シュガーリングです。

おそらく、これはサポートを保証するのに十分有用なアイデアではないでしょう (今のところ!)。しかし、それは言語の不完全さを修正します。

また、演算子のクラス オーバーロードが暗黙的に static であることも考慮しnewdeleteください。したがって、不完全性にはすでに少し例外があります。

于 2013-04-12T22:31:00.047 に答える
0

これが理由かもしれません。

それぞれにoperator1 つ以上のoperands. したがって、それを宣言する場合、オブジェクト(オペランド)を使用して呼び出すことstaticはできません。

オブジェクトに過ぎない何らかのオペランドでそれを呼び出すには、関数は非静的でなければなりません。

以下は、関数のオーバーロードを行う際に満たさなければならない条件です。

  • ユーザー定義型のオペランドが少なくとも 1 つ必要です。

したがって、演算子のオーバーロード関数を static として宣言するとします。すると、まず上記の条件が満たされなくなります。

もう 1 つの理由は、静的関数内では静的データ メンバーにしかアクセスできないことです。しかし、演算子のオーバーロードを行っている間は、すべてのデータ メンバーにアクセスする必要があります。したがって、演算子のオーバーロード関数を static として宣言すると、すべてのデータ メンバーにアクセスできなくなります。

したがって、演算子のオーバーロード関数はnon-static member function.

しかし、例外があります。

演算子のオーバーロードにフレンド関数を使用する場合、静的として宣言できます。

于 2012-08-10T03:06:40.403 に答える
0

静的演算子 + を許可することで発生する可能性のある直接的な欠点については認識していません (十分に長く考えると、いくつかの理論が得られるかもしれません)。しかし、少なくとも Bjarne Stroustrup が宣言した「使わないものにはお金を払わない」という原則は、すでに十分な答えだと思います。より複雑な構文を除いてその静的演算子が許可されると、何が得られますか (「+」だけでなく、どこにでも「X::operator+」を記述する必要があります)。

于 2013-07-31T13:26:42.217 に答える