演算子>= オーバーロードによる奇妙な動作
-
23-08-2019 - |
質問
C++ で演算子をオーバーロードすると、奇妙な動作が発生します。クラスがあり、その内容がlong double以上であるかどうかを確認する必要があります。このチェックを行うために >= 演算子をオーバーロードしました。私の宣言は次のとおりです。
bool MyClass::operator>=(long double value) const;
私のクラスには、特定の条件下でのみ例外なく動作する Cast-to-long-double 演算子もあると言わざるを得ません。この演算子を使用すると、コンパイラは、operator>= の使用があいまいであると警告し、代替手段は次のとおりです。
- 私の。
- 内蔵されている
operator>=(long double, int)
.
さて、プログラムにオペレータの使用を強制するにはどうすればよいでしょうか?
解決
2015 年の更新: または、変換機能を維持したい場合は、 (double)obj
代わりに構文 obj.to_double()
構文、変換関数を作成します explicit
そのキーワードを接頭辞として付けます。変換をトリガーするには、明示的なキャストが必要です。個人的には、 .to_double
構文 (変換が次のように行われる場合を除く) bool
その場合、変換は次のように使用されるためです。 if(obj)
たとえそうであったとしても explicit
, 、そしてそれはよりもはるかに読みやすいです if(obj.to_bool())
私の意見では。
変換演算子を削除します。ずっとトラブルの元になりますよ。のような機能を持っています
to_double()
または、double 値を返し、その関数を明示的に呼び出して double を取得する同様のものです。
現在の問題としては、次のような問題があります。
obj >= 10
その表現を考えてみましょう。組み込み演算子は、変換演算子 long double() を使用して型のユーザー定義の変換シーケンスによって最初の引数と一致します。ただし、関数は int から long double (整数から浮動小数点への変換) への標準変換シーケンスによって 2 番目の引数と一致します。2 つの引数の変換があるが、少なくとも 1 つの引数がより良く変換され、残りの引数が 1 回の呼び出しでより悪く変換されない場合、それは常にあいまいです。あなたの場合、組み込みのものは2番目の引数とよりよく一致しますが、最初の引数はより悪くなりますが、関数は最初の引数とよりよく一致しますが、2番目の引数はより悪くなります。
わかりにくいので、いくつかの例を示します (char から int への変換はプロモーションと呼ばれ、char から int 以外への変換 (変換と呼ばれる) よりも優れています)。
void f(int, int);
void f(long, long);
f('a', 'a');
最初のバージョンを呼び出します。最初の引数はすべてより適切に変換できるためです。同様に、次の場合でも最初のものを呼び出します。
void f(int, long);
void f(long, long);
f('a', 'a');
前者はより良く変換される可能性があり、2 番目はより悪く変換されることはないからです。しかし、次は、 曖昧な:
void f(char, long);
void f(int, char);
f('a', 'a'); // ambiguous
この場合はさらに興味深いです。最初のバージョンは、完全一致によって最初の引数を受け入れます。2 番目のバージョンは、完全一致によって 2 番目の引数を受け入れます。しかし、どちらのバージョンも、少なくとも同じように他の議論を受け入れていません。最初のバージョンでは 2 番目の引数の変換が必要ですが、2 番目のバージョンでは引数の昇格が必要です。したがって、プロモーションはコンバージョンよりも優れていますが、2 番目のバージョンへの呼び出しは失敗します。
上記のあなたのケースと非常によく似ています。標準的な変換シーケンス (int/float/double から long double への変換) であっても、 より良い ユーザー定義の変換シーケンス (MyClass から long double への変換) よりも、演算子のバージョンは選択されません。これは、他のパラメーター (long double) が引数からの変換を必要とするため、その引数に対して組み込み演算子が必要とする変換よりも悪いものであるためです (完璧にマッチ)。
C++ ではオーバーロードの解決は複雑な問題であるため、その微妙なルールをすべて覚えておくことは不可能です。しかし、大まかな計画を立てることは十分に可能です。お役に立てば幸いです。
他のヒント
あなたが効果的に明記されているdouble
への暗黙的な変換を提供することにより、私のクラスはdouble
に、オペレータに建設> = double
sのために使用されている場合、あなたが本当に気にしてはならない。この理由のために同等です。あなたがをした場合のケアを行い、その後、あなたのクラスは本当にdouble
と「同等」ではありません、あなたはdouble
するの暗黙のの変換を提供するが、代わりに明示的なのGetAsDouble、またはConvertToDoubleメンバ関数ます。
あなたは、現時点では、あいまいさを持っている理由は、t >= d
があなたのクラスのインスタンスであり、t
がダブルで表現d
のために、コンパイラは常に左側または右側のいずれかの変換を提供しなければならないということです右側はそう表現は本当に曖昧です。どちらのt
のoperator double
が呼び出されると、内蔵の演算子> = double
sのために使用されている、またはdがlong double
に昇格する必要がありますし、あなたのメンバ演算子は> =使用されます。
編集、あなたの変換がlongにダブルであり、あなたの比較がintに反していることを示唆するためにあなたの質問を更新しました。その場合、最後の段落を読んでください。
あなたは、現時点では、あいまいさを持っている理由は、t >= d
があなたのクラスのインスタンスであり、t
がd
で表現int
のために、コンパイラは常に左側または右側のいずれかの変換を提供しなければならないということです右側はそう表現は本当に曖昧です。 t
のoperator long double
が呼ばれ、ビルトイン演算子のいずれか> = long double
とint
のために使用されている、またはdはlong double
とあなたの会員事業者に昇格する必要があります> =使用されます。
リテラルと比較していると思います int
, ではなく、 long double
:
MyClass o;
if (o >= 42)
{
// ...
}
その場合、どちらの選択肢も同様に優れており、複雑です。
を使用して operator long double()
:
MyClass::operator long double()
- 内蔵
operator>=(long double, int)
を使用して MyClass::operator>=(long double)
:
- 内蔵変換
int
にlong double
MyClass::operator>=(long double)
あなたは宣言の中でlong double
を持っています。 double
にそれを変更してみてください。
カスタムキャストと組み合わせる演算子のオーバーロードの使用は、あなたのクラスのユーザーに非常に混乱することができます。自問してみて、このクラスのユーザーは、のそれは、二重に自分自身を変換、またはダブルと匹敵するの期待? .greaterThan(ダブル)関数は、同じ目標を達成するが、ユーザーを驚かせずに持っていないでしょうか。
私はあなたが常に明示的にあいまいさを避けるために、比較する前に倍増すると、あなたのオブジェクトをキャストすることができると思います。私があなただったらしかし、私は上記のアプローチを再考し、直感的だし、代わりに派手なタイプのキャストや演算子のオーバーロードの、驚くように動作し、コードを書くことに専念したい。
に演算子オーバーロードについて(FQAの素晴らしい暴言)
- 組み込み演算子>=(long double, int)。
次のように定義したようです:
bool class::operator>=(long double value) { return value >= classValue; }
そしてあなたは欠けています:
bool class::operator>=(double value) { return value >= classValue; }
bool class::operator>=(int value) { return value >= classValue; }
そのため、コンパイラはどちらの方法で変換するかを決定できません。(曖昧ですね。)
おそらくテンプレート化された関数 (またはメソッド) が役に立つでしょうか?
次のような状況に注意してください a>=b ~とは異なるメソッドを呼び出します b>=a.