三元运算符求值顺序
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(而不是 6.1)?
像下面这样更改代码返回值 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。为什么?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
条件运算符检查两个方向的转换。在这种情况下,由于您的构造函数是显式的(因此
?:
没有歧义),因此使用从Foo
到int
的转换,使用转换为double
的转换函数:这是可行的,因为在应用转换函数之后,标准转换将double
转换为int
(截断)如下。在您的情况下,?:
的结果是int
,并且值为6
。在第二种情况下,由于操作数的类型为
double
,因此不会发生到int
的尾随转换,因此结果类型为?:
类型double
具有预期值。要理解“不必要的”转换,您必须了解像
?:
这样的表达式是“与上下文无关”评估的:在确定它的值和类型时,编译器不会认为它是返回double
的函数的return
操作数。编辑:如果您的构造函数是隐式的,会发生什么情况?
?:
表达式将是不明确的,因为您可以将int
转换为Foo
类型的右值(使用构造函数),并且将 < code>Foo 转换为int
类型的右值(使用转换函数)。标准说解释如何将
Foo
转换为int
的段落:5.16/3
关于condition ? E1:E2:
4.3
关于“隐式转换”:8.5/14
关于复制初始化 (T t = e;
)13.3.1.5
关于候选转换函数The conditional operator checks conversions in both directions. In this case, since your constructor is explicit (so the
?:
is not ambiguous), the conversion fromFoo
toint
is used, using your conversion function that converts todouble
: That works, because after applying the conversion function, a standard conversion that converts thedouble
toint
(truncation) follows. The result of?:
in your case isint
, and has the value6
.In the second case, since the operand has type
double
, no such trailing conversion toint
takes place, and thus the result type of?:
has typedouble
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 areturn
for a function returning adouble
.Edit: What happens if your constructor is implicit? The
?:
expression will be ambiguous, because you can convert anint
to an rvalue of typeFoo
(using the constructor), and aFoo
to an rvalue of typeint
(using the conversion function). The Standard saysParagraphs explaining how your
Foo
is converted toint
:5.16/3
aboutcondition ? E1 : E2
:4.3
about "implicitly converted":8.5/14
about copy initialization (T t = e;
)13.3.1.5
about the conversion function candidates标准第 5.16 节对此进行了令人困惑的详细描述。重要的部分在第 3 段。“如果 E2 是左值:如果 E1 可以隐式转换(第 4 条)到类型“对 T2 的引用”,则 E1 可以转换为匹配 E2,但要遵守转换中的约束引用必须直接绑定(8.5.3)到 E1。”
在表达式中,唯一的左值是
item
,因此问题是 0(int)是否可以隐式转换为Foo
类型。在这种情况下,没有任何其他类型到Foo
的隐式转换,因为唯一可用的转换函数被标记为explicit
。因此,这是行不通的,我们接着“如果 E2 是右值,或者如果上面的转换无法完成:”(跳过关于它们是否都具有类类型的部分)“否则(即,如果 E1 或 E2具有非类类型,或者如果它们都具有类类型,但基础类不相同或者一个不是另一个的基类):如果 E1 可以隐式转换为表达式 E2 所用的类型,则 E1 可以转换为匹配 E1如果 E2 被转换为右值(或者它所具有的类型,如果 E2 是右值),则会出现这种情况。”因此,我们看到 0 是一个
int
类型的右值。我们可以转换Foo
,因为我们可以将Foo
隐式转换为double
,然后再转换为int
。然后:“利用该过程,判断第二操作数是否可以转换以匹配第三操作数,以及第三操作数是否可以转换以匹配第二操作数。如果两者都可以转换,或者其中一个可以转换,但是转换不明确,则程序格式错误。如果两者都无法转换,则操作数保持不变,并按如下所述执行进一步检查。如果可以进行一次转换,则将该转换应用于所选操作数和转换后的操作数。在本节的其余部分中,使用原始操作数来代替。”
由于我们可以将
Foo
转换为int
,因此我们将Foo
转换为int
来获取剩余的内容决心。我们现在有两个int
作为表达式类型,并且至少有一个是右值。我可以继续第 5 段和第 6 段,但我认为很明显该表达式的类型为
int
。我认为要点是:
您的编译器正在按照标准运行。
条件表达式类型的规则过于复杂,不容易掌握。不要挑战极限,因为有时你会犯错误。 (此外,这正是编译器可能无法精确实现标准的地方。)
尝试指定类型,以便第二个和第三个表达式具有相同的类型。无论如何,请尽量避免使用不属于所需类型的表达式。
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 typeFoo
. In this case, there is no implicit conversion of any other type to aFoo
, since the only available conversion function is markedexplicit
. 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 aFoo
, since we can implicitly convert aFoo
to adouble
and thence to anint
. 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 anint
, we convert theFoo
to anint
for the remainder of the determination. We've now got twoint
s 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:
Your compiler is functioning according to the standard.
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.)
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.
三元表达式的类型在编译时确定;运行时 some_condition 是什么并不重要。
我想问题是:为什么编译器在第一个示例中选择 int 而不是 double ?
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?
三元运算符根据其参数猜测类型。它不能将 item 转换为 int,但可以将 item 转换为 double,然后再将其转换为 int。
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.