operator==
C++ がクラスを自動的に定義できないことは理解していますが、使用できない場合!(a == b)
に使用できないのはなぜですか?a != b
operator!=
operator==
std::rel_ops
今日まで聞いたことがありませんでしたが、私は知っています。
operator==
C++ がクラスを自動的に定義できないことは理解していますが、使用できない場合!(a == b)
に使用できないのはなぜですか?a != b
operator!=
operator==
std::rel_ops
今日まで聞いたことがありませんでしたが、私は知っています。
operator==
は必ずしも の反対を意味するわけではないからですoperator!=
。
operator==
を意味しないインスタンスは考えられません!operator!=
が、それらは別個の演算子です。C++ について最も解放的で、時には最も苛立たしいことの 1 つは、C++ がコードの記述方法に関して最小限の制限セットを適用することです。operator==
の反対ではないインスタンスがある場合はoperator!=
、C++ で表現できるはずです。実際、できます。
C++ では、良いことも悪いことも受け入れます。これは「悪い」のセットにあると考えることができます。
ほとんどの場合、 を正しく実装するのは簡単なことであることに注意してoperator!=
くださいoperator==
。
bool Gizmo::operator!=(const Gizmo& rhs) const
{
return !operator==(rhs);
}
言語としての C++ は、ユーザーが明示的に要求しない機能を提供しません。この哲学がデフォルトのコンストラクターなどで少し壊れていることは知っていますが、これは Stroustrup が非常に早い段階で行った設計上の決定でした。そのため、コンパイラは、要求していないものを自動的に生成しません。
1993 年初頭に Bjarne からの電子メール チェーンへの参照がACCU Web サイトにあり、これについての言及が含まれています。私の記憶が正しければ、D&E にもあります。参考になるコピーが手元にありません。
言語はあなたが望むことをすることを許されていません。operator==
とoperator!=
は2つの異なる演算子です。!(x==y)
とが異なる結果をx!=y
もたらす例を考えることはできませんが、operator<=
vsを検討してoperator>
ください。なぜこれらの両方が必要なのですか?x<=y
と書くことができ!(x>y)
ますよね?間違い。
#include<iostream>
int main () {
double y = 0.0;
double x = y/y;
std::cout << " (x <= y) -> " << (x <= y) << "\n";
std::cout << "!(x > y) -> " << !(x > y) << "\n";
}
C++ の最初のバージョン (修正後の別名 C++03) では、C++ で C をコンパイルできるようにするために、デフォルト コンストラクター、コピー コンストラクター、コピー代入演算子、およびデストラクターの自動定義が導入されました。
デストラクタのカスタム定義を提供する多くの人々は、コピー コンストラクタと代入演算子を定義するのを忘れてしまい、ひざの上で混乱することになります。
隠し実行パスなどの暗黙的なメソッドは、開発者を混乱させるようです。そして、私たちは皆噛まれたと思います。
ただし、C++11 には、オンデマンドのデフォルト メソッドのための非常に巧妙なメカニズムがあります。
class Test { Test() = default; }; // a rather useless class...
したがって、C++03 のコンストラクターの自動生成などの教訓を踏まえて、この自動生成の導入には賛成しませんが、次のことは確実にサポートします。
bool operator!=(Test const&, Test const&) = default;
(operator==
範囲内で、明らかに)
同様に:
bool operator>(Test const&, Test const&) = default;
bool operator<=(Test const&, Test const&) = default;
bool operator>=(Test const&, Test const&) = default;
(operator<
範囲内で、明らかに)
しかし、ここで本当の質問をするかもしれません: より一般的なメソッドを提供しないのはなぜですか?
operator==
は一般的に失敗することはありませんが、私は の無数の壊れた実装を見てきましたoperator<
。どうやら弱い順序を尊重することは、見かけほど簡単ではありません (*)。それでも、タプルについて考えてみると、それは 2 つのタプルを辞書的に比較したにすぎません。
(*) andの実装が一致しない (つまり、通常は<=> ) がありますが、タプルはそれを解決します!==
<
!(a < b) and !(b < a)
a == b
実際:
std::tuple<int, std::string const&> to_tuple(Test const&);
一般に、初期演算子を生成するために使用できます。
template <typename T>
auto operator==(T const& left, T const& right) -> decltype(to_tuple(left), bool{}) {
return to_tuple(left) == to_tuple(right);
}
template <typename T>
auto operator<(T const& left, T const& right) -> delctype(to_tuple(right), bool{}) {
return to_tuple(left) < to_tuple(right);
}
それで、トラップは何ですか?さて、ADL。これらのテンプレートが、実装するクラスとは異なる名前空間に存在する場合、to_tuple
ADL によって自動的に取得されないため、物事がバラバラになります (同じ理由 whileusing std::swap
は非常に一般的です...)。
したがって、スコープ内にあるbool operator==(Test const&, Test const&) = default;
場合は正しいこと (tm) を実行する必要があると主張できます。to_tuple(Test const&)
それはクレイジーでさえありません。多すぎない程度に。
しかし、私が元の提案からどれだけ離れているか見てください。委員会の決定が最終的にどうなるか想像してみてください...
そしてその間に?
まあ、個人的には、私は実装します:
#define MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, Op_) \
inline bool operator Op_ (Type_ const& left, Type_ const& right) { \
return to_tuple(left) Op_ to_tuple(right); \
} \
#define MY_DEFINE_TUPLE_EQUAL(Type_) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, ==) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, !=)
#define MY_DEFINE_TUPLE_COMP(Type_) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, <) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, >) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, <=) \
MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, >=)
その後:
class Test;
std::tuple<int, std::string const&> to_tuple(Test const&); // or boost::tuple
MY_DEFINE_TUPLE_EQUAL(Test);
MY_DEFINE_TUPLE_COMP(Test);
ADL で動作し、インライン コードを生成しto_tuple
(それ自体がインラインである場合もそうでない場合もあります)、正確で一貫性のある実装を生成==
し、6 つのメソッドすべて<
よりもタイピングが少なくなります。= default
コンパイラ エラー メッセージのソースの場所も残します。
では...なぜ実際に言語をさらに複雑にするのでしょうか?