質問
class Foo {
public:
explicit Foo(double item) : x(item) {}
operator double() {return x*2.0;}
private:
double x;
}
double TernaryTest(Foo& item) {
return some_condition ? item : 0;
}
Foo abc(3.05);
double test = TernaryTest(abc);
上記の例で、some_conditionがtrueである場合、テストが(6.1ではなく)6に等しいのはなぜですか?
以下のようにコードを変更すると、6.1の値が返されます
double TernaryTest(Foo& item) {
return some_condition ? item : 0.0; // note the change from 0 to 0.0
}
(元の例では)Foo :: operator doubleからの戻り値はintにキャストされ、その後doubleに戻されるようです。なぜですか?
解決
条件演算子は、両方向の変換をチェックします。この場合、コンストラクターは明示的であるため(?:
は曖昧ではありません)、 Foo
から int
への変換が使用されます。 double
に変換する変換関数:変換関数を適用した後、 double
を int
に変換する標準変換(トランケーション)が続きます。あなたの場合の?:
の結果は int
で、値は 6
です。
2番目の場合、オペランドのタイプは double
であるため、 int
への後続の変換は行われず、結果の型は?:
のタイプは double
で、期待される値が含まれています。
「不要」を理解するには変換では、?:
のような式が&quot; context-free&quot;として評価されることを理解する必要があります:値と型を決定する際、コンパイラは<のオペランドであるとは見なしません double
を返す関数の場合はcode> return 。
編集:コンストラクタが暗黙的の場合はどうなりますか? ?:
式はあいまいになります。これは、 int
を Foo
型の右辺値(コンストラクターを使用)に変換し、< code> Foo を int
型の右辺値に変換します(変換関数を使用)。標準は言う
このプロセスを使用して、第2オペランドを第3オペランドに一致するように変換できるかどうか、および第3オペランドを第2オペランドに一致するように変換できるかどうかを判断します。両方を変換できる場合、または一方を変換できるが変換があいまいな場合、プログラムの形式は正しくありません。
Foo
が int
に変換される方法を説明する段落:
conditionについての 5.16 / 3
E1:E2
:
それ以外の場合、2番目と3番目のオペランドの型が異なり、どちらかが(おそらくcv修飾された)クラス型である場合、それらの各オペランドを他方の型に変換しようとします。 [...] E2が右辺値に変換された場合にE1を式E2が持つ型に暗黙的に変換できる場合、E1はE2に一致するように変換できます(E2が右辺値である場合は、それが持つ型)。
4.3
「暗黙的に変換された」について:
コピーの初期化に関する式eは、宣言
T t = e;
が整形式の一時変数tである場合に限り、暗黙的に型Tに変換できます。
8.5 / 14
( T t = e;
)
変換関数の候補についてソースタイプが(おそらくcvで修飾された)クラスタイプである場合、変換関数が考慮されます。適用可能な変換関数が列挙され(13.3.1.5)、最適な変換関数はオーバーロード解決(13.3)によって選択されます。そのように選択されたユーザー定義の変換が呼び出され、初期化式を初期化されるオブジェクトに変換します。変換を実行できないかあいまいな場合、初期化の形式は正しくありません。
13.3.1.5
Sとその基本クラスの変換関数が考慮されます。 S内に隠されておらず、タイプTまたは標準変換シーケンス(13.3.3.1.1)を介してタイプTに変換できるタイプを生成するものが候補関数です。
他のヒント
これは、標準のセクション5.16で明らかに紛らわしい詳細でカバーされています。重要な部分は段落3にあります。&quot; E2が左辺値の場合:E1を暗黙的に変換できる場合(E4を4項)、E2に一致するように変換できます。参照はE1に直接(8.5.3)バインドする必要があります。&quot;
式では、左辺値は item
のみであるため、問題は0(int)を暗黙的に Foo
型に変換できるかどうかです。この場合、唯一の利用可能な変換関数は explicit
とマークされているため、他の型から Foo
への暗黙的な変換はありません。したがって、それは機能せず、&quot; E2が右辺値である場合、または上記の変換を実行できない場合:&quot;に従います。 (両方ともクラス型を持っているかどうかについては省略)&quot;それ以外の場合(つまり、E1またはE2がクラス型を持たない場合、または両方ともクラス型を持っているが、基になるクラスが同じまたはベースクラスのいずれでもない場合)その他):E2が右辺値に変換された場合にE1を式E2が持つ型に暗黙的に変換できる場合、E1はE1に一致するように変換できます(または、E2が右辺値である場合は、それが持つ型)&quot;
したがって、0は int
型の右辺値です。 Foo
を暗黙的に Foo
を double
に変換し、それから int
に変換できるため、 Foo
を変換できます。次に:
&quot;このプロセスを使用して、第2オペランドを第3オペランドに一致するように変換できるかどうか、および第3オペランドを第2オペランドに一致するように変換できるかどうかを判断します。両方を変換できる場合、または1つを変換できるが、変換があいまいな場合、プログラムの形式は正しくありません。どちらも変換できない場合、オペランドは変更されずに残り、以下で説明するようにさらにチェックが実行されます。正確に1つの変換が可能な場合、その変換は選択されたオペランドに適用され、変換されたオペランドはこのセクションの残りの元のオペランドの代わりに使用されます。&quot;
Foo
を int
に変換できるため、 Foo
を int
に変換します決定の残り。式タイプとして2つの int
があり、少なくとも1つは右辺値です。
段落5と6に進むことができますが、式の型が int
であることは明らかだと思います。
要点は次のとおりです:
-
お使いのコンパイラは標準に従って機能しています。
-
条件式のタイプに関するルールは複雑すぎて簡単に学習できません。いつか間違えるから、封筒を押さないでください。 (さらに、これは、コンパイラが標準を正確に実装できない可能性がある場所です。)
-
2番目と3番目の式が同じ型になるように型を指定してください。いずれの場合でも、目的のタイプではない式を避けるようにしてください。
三項式のタイプはコンパイル時に決定されます。実行時にsome_conditionが何であるかは関係ありません。
質問は、最初の例でコンパイラがdoubleではなくintを選択するのはなぜでしょうか?
三項演算子は、引数から型を推測します。アイテムをintに変換することはできませんが、アイテムをdoubleに変換してからintに変換できます。