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为真,为什么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。为什么呢?
答案 0 :(得分:9)
条件运算符检查两个方向的转换。在这种情况下,由于您的构造函数是显式的(因此?:
不明确),因此使用转换为{{1}的转换函数,使用从Foo
到int
的转换。 }:这是有效的,因为在应用转换函数后,会发生将double
转换为double
(截断)的标准转换。您的案例中int
的结果为?:
,其值为int
。
在第二种情况下,由于操作数的类型为6
,因此不会发生double
的此类跟踪转换,因此int
的结果类型的类型为?:
具有预期价值。
要理解“不必要的”转换,您必须了解像double
这样的表达式被评估为“无上下文”:在确定它的值和类型时,编译器不会认为它是返回?:
的函数的return
的操作数。
编辑:如果您的构造函数是隐式会发生什么? double
表达式不明确,因为您可以将?:
转换为类型为int
的rvalue(使用构造函数),将Foo
转换为类型{的右值{1}}(使用转换功能)。标准说
使用此过程,确定是否可以转换第二个操作数以匹配第三个操作数,以及是否可以转换第三个操作数以匹配第二个操作数。如果两者都可以转换,或者可以转换,但转换不明确,则程序格式不正确。
段落说明了Foo
如何转换为int
:
Foo
关于int
:
否则,如果第二个和第三个操作数具有不同的类型,并且具有(可能是cv限定的)类类型,则尝试将每个操作数转换为另一个操作数的类型。 [...] E1可以转换为匹配E2,如果E1可以隐式转换为表达式E2,如果E2被转换为右值(或者它具有的类型,如果E2是右值),则可以将其转换为。
5.16/3
关于“隐式转换”:
当且仅当声明
condition ? E1 : E2
格式正确时,表达式e才能隐式转换为类型T,对于某些发明的临时变量t。
4.3
关于复制初始化(T t = e;
)
关于转换函数候选的如果源类型是(可能是cv限定的)类类型,则考虑转换函数。列举了适用的转换函数(13.3.1.5),并通过重载决策(13.3)选择最佳函数。调用如此选择的用户定义转换以将初始化表达式转换为正在初始化的对象。如果无法进行转换或模糊不清,则初始化结构不正确。
8.5/14
考虑S及其基类的转换函数。那些未隐藏在S和产量类型T中的类型或可通过标准转换序列(13.3.3.1.1)转换为类型T的类型是候选函数。
答案 1 :(得分:7)
标准第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
非常明显。
我认为要点是:
您的编译器正在按照标准运行。
关于条件表达式类型的规则太复杂,不易学习。不要推开信封,因为你有时会犯错误。 (此外,这正是编译器可能无法准确实现标准的那种地方。)
尝试指定类型,以便第二个和第三个表达式具有相同的类型。在任何情况下,尽量避免使用不是所需类型的表达式。
答案 2 :(得分:1)
三元表达式的类型在编译时确定; some_condition在运行时是什么并不重要。
我想问题是:为什么编译器在第一个例子中选择int而不是double?
答案 3 :(得分:0)
三元运算符从其参数中猜出类型。它不能将item转换为int,但它可以将item转换为double,然后将其转换为int。