次の短い 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()
変換は必要ありませんが、to変換がoperator int()
必要になるため、コンパイラは最初のものを選択する必要があります。それに対して何かを行いますが、const 変換はコンパイラによってより「高価」であると見なされますか?int
bool
const
を削除するとconst
、すべてのコンパイラが同じようfalse
に出力を生成します。一方、2 つのクラスを組み合わせると (両方の演算子が同じクラスになります)、3 つのコンパイラすべてがtrue
出力を生成します。
ベストアンサー1
標準では次のように規定されています。
派生クラスの変換関数は、2 つの関数が同じ型に変換されない限り、基本クラスの変換関数を非表示にしません。
§12.3 [クラス.conv]
つまり、 はoperator bool
によって隠されていませんoperator int
。
標準では次のように規定されています。
オーバーロード解決中、暗黙のオブジェクト引数は他の引数と区別できません。
§13.3.3.1 [オーバーマッチ関数]
この場合の「暗黙のオブジェクト引数」は でb
、これは 型ですB2 &
。operator bool
は を必要とするため、コンパイラは を呼び出すためconst B2 &
に に const を追加する必要があります。これにより、他の条件がすべて同じであれば、より適切な一致が実現します。b
operator bool
operator int
標準では、次の場合にstatic_cast
(このインスタンスで C スタイルのキャストが実行している) を型T
(この場合はint
) に変換できると規定されています。
何らかの発明された一時変数に対する宣言は
T t(e);
整形式ですt
。§5.2.9 [式.静的.キャスト]
したがって、int
は に変換できbool
、 はbool
に同様に変換できますbool
。
標準では次のように規定されています。
とその基底クラスの変換関数
S
が考慮される。 と 内に隠されていない非明示的な変換関数はS
、型を生成する。T
T
または、標準の変換シーケンスを介して型に変換できる型候補関数です。§13.3.1.5 [オーバーマッチコンバージョン]
したがって、オーバーロード セットはoperator int
とで構成されますoperator bool
。他の条件がすべて同じであれば、 のoperator int
方が一致度が高くなります (const を追加する必要がないため)。したがってoperator int
を選択する必要があります。
注意: (おそらく直感に反して) 標準では、オーバーロード セット (上記で確立) に追加された後は、一方の引数の変換シーケンスがもう一方の引数の変換シーケンスよりも優れている場合 (const のため、このインスタンスの場合はその通り)、戻り値の型 (つまり、これらの演算子が変換する型) は考慮されません。
標準では次のように規定されています。
これらの定義によれば、実行可能な関数F1は、すべての引数iに対してICSi(F1)がICSi(F2)よりも悪い変換シーケンスではない場合、他の実行可能な関数F2よりも優れた関数であると定義され、
- ある引数jに対して、ICSj(F1)はICSj(F2)よりも優れた変換シーケンスである。そうでない場合は、
- コンテキストはユーザー定義の変換による初期化であり、戻り値の型 F1 から宛先の型 (つまり、初期化されるエンティティの型) への標準変換シーケンスは、戻り値の型 F2 から宛先の型への標準変換シーケンスよりも優れた変換シーケンスです。
§13.3.3 [オーバーマッチベスト]
この場合、引数は 1 つだけです (暗黙のパラメータ)。 => ( を呼び出す)this
の変換シーケンスは=> ( を呼び出す)よりも優先されるため、実際には に直接変換されないという事実に関係なく、オーバーロード セットから が選択されます。B2 &
B2 &
operator int
B2 &
const B2 &
operator bool
operator int
bool