1. ホーム
  2. c++

[解決済み] コンパイラによって呼び出されるキャスト演算子が異なる

2023-08-04 20:30:30

質問

次のような短いC++プログラムを考えてみましょう。

#include <iostream>

class B {
public:
    operator bool() const {
        return false;
    }
};

class B2 : public B {
public:
    operator int() {
        return 5;
    }
};

int main() {
    B2 b;
    std::cout << std::boolalpha << (bool)b << std::endl;
}

異なるコンパイラでコンパイルすると、様々な結果が得られます。Clang 3.4 と GCC 4.4.7 では、次のように表示されます。 true と表示され、Visual Studio 2013では false で異なるキャスト演算子を呼び出していることを意味します。 (bool)b . 規格上、どちらが正しい動作なのでしょうか?

私の理解では operator bool() は変換を必要としませんが operator int() は変換が必要ですが int から bool に変換されるので、コンパイラは最初のものを選ぶはずです。は const はそれで何かをするのでしょうか、コンパイラはconst-conversionをよりquot;expensive"と見なすのでしょうか。

もし私が const を削除すると、すべてのコンパイラが等しく false を出力します。 一方、2つのクラスを一緒にすると(両方の演算子が同じクラスになる)、3つのコンパイラとも、出力として true を出力します。

どのように解決するのですか?

規格に記載されています。

派生クラス内の変換関数は、2つの関数が同じ型に変換しない限り、基底クラス内の変換関数を隠さない。

§12.3 [class.conv]を参照してください。

ということは operator bool で隠れないということです。 operator int .

規格に記載されています。

オーバーロードの解決中、暗黙のオブジェクト引数は他の引数と区別がつかない。

§13.3.3.1 [over.match.func]を参照してください。

この場合の "暗黙のオブジェクト引数" は次のとおりです。 b であり、型は B2 & . operator bool が必要です。 const B2 & を必要とするので、コンパイラは const を b を呼び出すために operator bool . これによって -- 他のすべての条件が同じであれば -- operator int の方がより良い一致となります。

規格では static_cast (この例では C スタイルのキャストが実行されています) に変換して T に変換することができます (この例では int ) の場合。

宣言 T t(e); は整形式で、ある発明された一時変数に対して t .

§5.2.9節 [expr.static.cast]を参照してください。

そのため int に変換される可能性があります。 bool に、そして bool は同様に bool .

規格に記載されています。

の変換関数は S とその基底クラスの変換関数が考慮される。の中に隠されていないそれらの非明示的な変換関数が考慮される。 S の中に隠されておらず,型 T またはタイプに変換可能なタイプ T に変換できる型であれば、標準的な変換シーケンスで が候補となる関数です。

§13.3.1.5節 [over.match.conv]を参照してください。

つまり、オーバーロード・セットは operator intoperator bool . 他の条件がすべて同じなら operator int の方がより良くマッチします(constnessを追加する必要がないので)。 したがって operator int が選択されるべきです。

標準では、(おそらく直感に反して)一旦オーバーロード集合に追加されると、(上記で確立されたように)戻り値の型(すなわち、これらの演算子が変換する型)を考慮しないことに注意。ただし、それらのうちの一方の引数の変換シーケンスが他方の引数の変換シーケンスより優れている場合(これは、恒常性のためにこの例ではそうである)である。

規格では

これらの定義を考えると、実行可能な関数F1は、すべての引数iについて、ICSi(F1)がICSi(F2)より悪い変換列でなければ、別の実行可能な関数F2より良い関数であると定義され、そして、次に

  • ある引数 j に対して、ICSj(F1) は ICSj(F2) よりも良い変換シーケンスであるか、または、そうでなければ。
  • の場合、コンテキストはユーザ定義変換による初期化であり、F1の戻り型から目的型(すなわち、初期化される実体の型)への標準変換シーケンスは、F2の戻り型から目的型への標準変換シーケンスより良い変換シーケンスである。

§13.3.3節 [over.match.best]の項参照。

この場合、引数は1つだけです(暗黙的な this パラメータ) のみです。 の変換シーケンスは B2 & => B2 & (を呼び出すには operator int ) よりも優れています。 B2 & => const B2 & (を呼び出すには operator bool )であるため operator int に直接変換されないという事実を気にすることなく、 オーバーロードのセットから選択されます。 bool .