Ternary operator evaluation order

ぃ、小莉子 提交于 2019-12-24 05:15:06

问题


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);

In the above example, why is test equal to 6 (instead of 6.1) if some_condition is true?

Changing the code like below returns value of 6.1

double TernaryTest(Foo& item) {
  return some_condition ? item : 0.0; // note the change from 0 to 0.0
}

It seems that (in the original example) the return value from Foo::operator double is cast to an int and then back to a double. Why?


回答1:


The conditional operator checks conversions in both directions. In this case, since your constructor is explicit (so the ?: is not ambiguous), the conversion from Foo to int is used, using your conversion function that converts to double: That works, because after applying the conversion function, a standard conversion that converts the double to int (truncation) follows. The result of ?: in your case is int, and has the value 6.

In the second case, since the operand has type double, no such trailing conversion to int takes place, and thus the result type of ?: has type double with the expected value.

To understand the "unnecessary" conversions, you have to understand that expressions like your ?: are evaluated "context-free": When determining the value and type of it, the compiler doesn't consider that it's the operand of a return for a function returning a double.


Edit: What happens if your constructor is implicit? The ?: expression will be ambiguous, because you can convert an int to an rvalue of type Foo (using the constructor), and a Foo to an rvalue of type int (using the conversion function). The Standard says

Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, or one can be converted but the conversion is ambiguous, the program is ill-formed.


Paragraphs explaining how your Foo is converted to int:

5.16/3 about condition ? E1 : E2:

Otherwise, if the second and third operand have different types, and either has (possibly cv-qualified) class type, an attempt is made to convert each of those operands to the type of the other. [...] E1 can be converted to match E2 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to an rvalue (or the type it has, if E2 is an rvalue).

4.3 about "implicitly converted":

An expression e can be implicitly converted to a type T if and only if the declaration T t = e; is well-formed, for some invented temporary variable t.

8.5/14 about copy initialization ( T t = e; )

If the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

13.3.1.5 about the conversion function candidates

The conversion functions of S and its base classes are considered. Those that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions.




回答2:


This is covered in positively confusing detail in section 5.16 of the standard. The important part is in paragraph 3. "If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (clause 4) to the type 'reference to T2', subject to the constraint that in the conversion the reference must bind directly (8.5.3) to E1."

In the expression, the only lvalue is item, so the question is whether 0 (an int) can be implicitly converted to type Foo. In this case, there is no implicit conversion of any other type to a Foo, since the only available conversion function is marked explicit. Therefore, that doesn't work, and we follow with "if E2 is an rvalue, or if the conversion above cannot be done:" (skipping the part about if they both have class type) "Otherwise (i.e., if E1 or E2 has a nonclass type, or if they both have class types but the underlying classes are not either the same or one a base class of the other): E1 can be converted to match E1 if E1 can be implicitly converted to the type that expression E2 would have if E2 were converted to an rvalue (or the type it has, if E2 is an rvalue)."

Therefore, we see that 0 is an rvalue, of type int. We can convert a Foo, since we can implicitly convert a Foo to a double and thence to an int. Then:

"Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, oor one can be converted but the conversion is ambiguous, the program is ill-formed. If neither can be converted, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, that conversion is applied to the chosen operand and the converted operand is used in the place of the original operand for the remainder of this section."

Since we can convert a Foo to an int, we convert the Foo to an int for the remainder of the determination. We've now got two ints as expression types, and at least one is an rvalue.

I can go on with paragraph 5 and 6, but I think it's pretty obvious that the expression has type int.

I think the takeaways are:

  1. Your compiler is functioning according to the standard.

  2. The rules on the type of a conditional expression are too complicated to be easily learned. Don't push the envelope, because you'll make a mistake sometime. (Besides, this is exactly the sort of place where a compiler might fail to implement the standard precisely.)

  3. Try to specify types so that both the second and third expression are of the same type. In any case, try to avoid expressions that are not of the desired type.




回答3:


The type of the ternary expression is determined at compile-time; it doesn't matter what some_condition is at runtime.

I guess the question is then: why does the compiler choose int instead of double in the first example?




回答4:


ternary operator guesses the type from its arguments. it cannot convert item to int but it can convert item to double which it then converts to int.



来源:https://stackoverflow.com/questions/1445274/ternary-operator-evaluation-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!