😊

C++で`*A == *B`の比較がC2666エラーになる原因と解決方法の一例

2024/11/28に公開

環境前提

下記で起きました。

  • Visual C++ 2022 MFC 14.3
  • C++20

MFCであることは多分関係ないです。

はじめに

C++でポインタのデリファレンスを利用してオブジェクトを比較しようとした際に、以下のようなエラーに直面しました。

エラー例
重大度レベル	コード	説明	プロジェクト	ファイル	行	抑制状態	詳細
エラー	C2666	'CHoge::operator ==': オーバーロードされた関数は同様の変換を行います code huhuhu.cpp 810

このエラーの発生時にはビルド出力に下記が出力されました。

出力例
huhuhu.cpp(n,m): error C2666: 'CHoge::operator ==': オーバーロードされた関数は同様の変換を行います
1>    source.h(i,j):
1>    'BOOL CHoge::operator ==(const CHoge &)' の可能性があります
1>    source.h(i,j):
1>    または 'BOOL CHoge::operator ==(const CHoge &)' [合成された式 'y = = x']
1>    huhuhu.cpp(n,m):
1>    引数リスト '(CHoge, CHoge)' を一致させようとしているとき

このエラーが発生する原因と、その解決方法の一例について解説します。

免責

本稿はChatGPTを用いて原因を検証していった際のまとめです。
解説した方法で解決したことは事実ですが、その他にはハルシネーションが発生している可能性があることをご了承ください。
本稿の解説を鵜呑みにして発生した問題についてその一切に責任を持ちません。


エラーの再現コード

問題となったコード

以下は、エラーを再現するコードの例です。

class CHoge {
public:
    BOOL operator==(const CHoge& other); // const 修飾なし
};

int main() {
    CHoge* A;
    CHoge* B;

    if (*A == *B) { // この行でエラーが発生
        // 比較処理
    }

    return 0;
}

このコードをコンパイルすると、operator== の呼び出しが曖昧であるというエラーが発生します。


エラーの原因

エラーが発生した主な原因は、operator== メンバ関数が const 修飾されていなかったことです。

なぜ const が必要なのか?

C++では、const 修飾が付いていないメンバ関数は、const なオブジェクトから呼び出すことができません。
*A*B のようなポインタのデリファレンス結果は一時オブジェクトとして扱われ、暗黙的に const な参照として解釈されることがあります。このとき、非 constoperator== を呼び出そうとすると、以下のような曖昧さが発生します:

  1. const 修飾がないため、一時オブジェクトに対して適切な operator== が見つからない。
  2. 比較のために一時的に生成される参照の扱いが不明確になり、エラーが発生する。

解決方法

修正後のコード

以下のように、const 修飾を追加することで問題を解決できます。

修正後の CHoge クラス

class CHoge {
public:
    BOOL operator==(const CHoge& other) const; // const 修飾を追加
};

修正後の operator== の定義

BOOL CHoge::operator==(const CHoge& other) const { // const 修飾を追加
    // 比較ロジック
    return TRUE;
}

この修正により、const なオブジェクトや参照を使用した場合でも、operator== を正しく呼び出せるようになります。


まとめ

問題の原因

  • operator==const 修飾がなかったこと。
    • ポインタのデリファレンス結果が const として扱われる場合があり、非 const メンバ関数が呼び出せなかった。

修正のポイント

  • const 修飾を付与することで、比較処理が const なオブジェクトでも動作するようにする。
    • BOOL operator==(const CHoge& other) const;

結果

この修正により、ポインタのデリファレンス結果を使った比較(*A == *B)でもエラーが発生せず、期待通りの動作をするようになりました。

あとがき

海外サイトも英語で探してみたけどてんでこの理由で引っかかってるケースを取り上げてなかった。
(もちろん俺の目に入った範囲でという話だが。)
おかげさまで内心涙目になりながら解決方法を探してひたすらGPTで壁打ちしてました。
解決方法見つかったのでよかったぁ。
理屈も別途調べて嘘も破綻もなさそうだからこれで良し。
こういう壁打ちができるのは良いよねAI。
比較オペレータはコピーを作成する意味が無いので特殊な場合除き全部constでええんちゃうかな。
近年のプログラマーなら常識なのかもだが、俺は内部的にはVC++2015のMFCのコードしか基本弄らんので知らんかった。
VS2022のインストールも頭の固い阿呆のせいで色々面倒だったが、それは別の話。

Discussion