三元运营商评估订单

Chr*_*ski 2 c++ ternary-operator ternary

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);
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,如果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
}
Run Code Online (Sandbox Code Playgroud)

似乎(在原始示例中)来自Foo :: operator double的返回值被强制转换为int,然后返回到double.为什么?

Joh*_*itb 9

条件运算符检查两个方向的转换.在这种情况下,因为你的构造是明确的(所以?:一点也不含糊),从转换Fooint使用,使用转换为转换功能double:这样的作品,因为应用转换功能,即转换标准转换后double,以int(截断)如下.?:在你的情况下的结果是int,并具有价值6.

在第二种情况下,由于操作数具有类型double,因此不会发生此类跟踪转换int,因此?:具有double期望值的类型的结果类型.

要理解"不必要的"转换,您必须了解像您这样的表达式?:被评估为"无上下文":在确定它的值和类型时,编译器不会认为它是return返回a的函数的操作数.double.


编辑:如果您的构造函数是隐式的,会发生什么?的?:表达将是不明确的,因为可以一个转换int到类型的右值Foo(使用构造),以及Foo以式的右值int(使用转换函数).标准说

使用该过程,确定是否可以转换第二操作数以匹配第三操作数,以及是否可以转换第三操作数以匹配第二操作数.如果两者都可以转换,或者一个可以转换,但转换不明确,则程序格式不正确.


段落解释了如何Foo转换为int:

5.16/3关于condition ? E1 : E2:

否则,如果第二个和第三个操作数具有不同的类型,并且具有(可能是cv-qualified)类类型,则尝试将每个操作数转换为另一个操作数的类型.如果E1可以隐式转换为E2表达式E2,如果E2被转换为右值(或者它具有的类型,如果E2是右值),则可以将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的类型是候选函数.


Dav*_*ley 7

这标准的第5.16节中包含了令人困惑的细节.重要的部分在第3段."如果E2是左值:如果E1可以隐式转换(第4节)到类型'对T2的引用',E1可以转换为匹配E2,受制于转换中的约束条件引用必须直接绑定(8.5.3)到E1."

在表达式中,唯一的左值是item,所以问题是0(一个int)是否可以隐式转换为类型Foo.在这种情况下,没有任何其他类型的隐式转换为a Foo,因为标记了唯一可用的转换函数explicit.因此,这不起作用,我们遵循"如果E2是右值,或者如果上面的转换不能完成:"(跳过关于它们是否都有类型的部分)"否则(即,如果E1或E2具有非类型类型,或者如果它们都具有类类型但基础类不是相同或者是另一个类的基类):如果E1可以隐式转换为表达式E2的类型,则E1可以转换为匹配E1如果E2被转换为右值(或者它具有的类型,如果E2是右值),则会有.

因此,我们看到0是一个类型的右值int.我们可以转换a Foo,因为我们可以隐式地将a转换Foo为a double,然后转换为a int.然后:

"使用此过程,确定是否可以转换第二个操作数以匹配第三个操作数,以及是否可以转换第三个操作数以匹配第二个操作数.如果两者都可以转换,则可以转换一个,但转换是程序是不明确的.如果两者都不能转换,操作数保持不变,进行如下所述的进一步检查.如果只能进行一次转换,则转换应用于所选操作数,并使用转换后的操作数代替本节其余部分的原始操作数."

既然我们可以转换Fooint,我们转换Fooint了确定的其余部分.我们现在有两个ints作为表达式类型,并且至少有一个是rvalue.

我可以继续第5和第6段,但我认为表达式的类型非常明显int.

我认为要点是:

  1. 您的编译器正在按照标准运行.

  2. 关于条件表达式类型的规则太复杂,不容易学习.不要推开信封,因为你有时会犯错误.(此外,这正是编译器可能无法准确实现标准的那种地方.)

  3. 尝试指定类型,以便第二个和第三个表达式具有相同的类型.在任何情况下,请尽量避免使用不是所需类型的表达式.