2
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の場合、なぜtestは(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に戻されるようです。なんで?

4

4 に答える 4

9

条件演算子は、両方向の変換をチェックします。この場合、コンストラクターは明示的であるため(したがって、?:はあいまいではありません)、からFooへの変換は、にint変換する変換関数を使用して使用されますdouble。これは、変換関数を適用した後、をに変換する標準の変換であるため、機能しdoubleますint。切り捨て)が続きます。?:あなたの場合の結果はでintあり、値はです6

2番目のケースでは、オペランドの型がであるdoubleため、このような末尾の変換intは行われず、結果の型は期待値の?:型になります。double

「不必要な」変換を理解するには、あなたのような式が「文脈自由」で評価されることを理解する必要があります。その値とタイプを決定するとき、コンパイラはそれがaを返す関数?:のオペランドであるとは見なしません。 。returndouble


編集:コンストラクターが暗黙的である場合はどうなりますか?を型の右辺値に(コンストラクターを使用して)変換し、aを型の右辺値に(変換関数を使用して)変換?:できるため、式はあいまいになります。規格によるとintFooFooint

このプロセスを使用して、第2オペランドを第3オペランドに一致するように変換できるかどうか、および第3オペランドを第2オペランドに一致するように変換できるかどうかを判別します。両方を変換できる場合、または一方を変換できるが変換があいまいな場合、プログラムの形式が正しくありません。


Fooあなたがどのように変換されるかを説明する段落int

5.16/3についてcondition ? E1 : E2

それ以外の場合、2番目と3番目のオペランドの型が異なり、どちらかが(おそらくcv修飾された)クラス型である場合、これらのオペランドのそれぞれを他の型に変換しようとします。[...] E2が右辺値に変換された場合に式E2が持つタイプ(またはE2が右辺値の場合はそのタイプ)にE1を暗黙的に変換できる場合、E1はE2と一致するように変換できます。

4.3「暗黙的に変換された」について:

T t = e;いくつかの発明された一時変数tについて、宣言が整形式である場合に限り、式eを暗黙的に型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に変換できるタイプを生成するものが候補関数です。

于 2009-09-18T15:36:23.527 に答える
7

これは、規格のセクション5.16で明確に紛らわしい詳細でカバーされています。重要な部分は段落3にあります。「E2が左辺値の場合:E1が暗黙的に「T2への参照」タイプに変換できる場合(4節)、E1はE2と一致するように変換できます。参照はE1に直接(8.5.3)バインドする必要があります。」

式では、左辺値は。のみitemであるため、問題は0(int)を暗黙的に型に変換できるかどうかFooです。Fooこの場合、使用可能な唯一の変換関数がマークされているため、他のタイプからの暗黙の変換はありませんexplicit。したがって、それは機能せず、「E2が右辺値である場合、または上記の変換を実行できない場合:」(両方がクラスタイプであるかどうかについての部分をスキップします)「それ以外の場合(つまり、E1またはE2の場合)非クラス型がある場合、または両方にクラス型があるが、基になるクラスが同じでないか、一方が他方の基本クラスではない場合):E1を式E2の型に暗黙的に変換できる場合は、E1をE1に一致するように変換できます。 E2が右辺値(または、E2が右辺値の場合はその型)に変換された場合に発生します。」

したがって、0は型の右辺値であることがわかりますintFoo暗黙的にaFooをaに変換しdouble、そこからをに変換できるため、を変換できintます。それで:

「このプロセスを使用して、第2オペランドを第3オペランドに一致するように変換できるかどうか、および第3オペランドを第2オペランドに一致するように変換できるかどうかを判断します。両方を変換できる場合、または一方を変換できますが、変換はあいまいな場合、プログラムの形式が正しくありません。どちらも変換できない場合、オペランドは変更されず、以下に説明するようにさらにチェックが実行されます。変換が1つだけ可能な場合は、その変換が選択されたオペランドに適用され、変換されたオペランドが使用されます。このセクションの残りの部分では、元のオペランドの代わりに。」

aFooをに変換できるので、残りの決定のためにをintに変換します。式タイプとして2つのがあり、少なくとも1つは右辺値です。Foointint

段落5と6を続けることができますが、式がタイプを持っていることはかなり明白だと思いますint

要点は次のとおりです。

  1. コンパイラは標準に従って機能しています。

  2. 条件式の型に関する規則は複雑すぎて簡単に習得できません。いつか間違えるので、封筒を押さないでください。(さらに、これはまさに、コンパイラーが標準を正確に実装できない可能性がある場所のようなものです。)

  3. 2番目と3番目の式の両方が同じタイプになるようにタイプを指定してみてください。いずれの場合も、目的のタイプではない式は避けてください。

于 2009-09-18T15:57:56.453 に答える
1

三項式のタイプは、コンパイル時に決定されます。実行時にsome_conditionが何であるかは関係ありません。

問題は、最初の例でコンパイラがdoubleではなくintを選択するのはなぜかということだと思います。

于 2009-09-18T15:31:14.760 に答える
0

三項演算子は、引数から型を推測します。itemをintに変換することはできませんが、itemをdoubleに変換してからintに変換することはできます。

于 2009-09-18T15:49:42.110 に答える