代码之家  ›  专栏  ›  技术社区  ›  Chris Bednarski

三元运算符评估顺序

  •  2
  • Chris Bednarski  · 技术社区  · 15 年前
    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);
    

    在上面的例子中,如果某些条件为真,为什么测试等于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。为什么?

    4 回复  |  直到 15 年前
        1
  •  9
  •   Johannes Schaub - litb    15 年前

    条件运算符检查两个方向的转换。在这种情况下,由于构造函数是显式的(因此 ?: 不含糊),从 Foo int 使用,使用转换为 double :这是有效的,因为应用转换函数后,标准转换将 双重的 int (截断)如下。结果 ?: 在你的情况下 int ,并且具有值 6 .

    在第二种情况下,因为操作数的类型为 双重的 ,没有这样的尾随转换 int 发生,因此结果类型为 ?: 有类型 双重的 具有预期值。

    要理解“不必要的”转换,您必须理解像 ?: 被评估为“无上下文”:在确定其值和类型时,编译器不会认为它是 return 对于返回 双重的 .


    编辑:如果构造函数是 隐性的 ?这个 ?: 表达式将不明确,因为可以将 int 到类型的右值 FOO公司 (使用构造函数),以及 到类型的右值 int (使用转换函数)。标准规定

    使用此过程,可以确定第二个操作数是否可以转换为与第三个操作数匹配,以及第三个操作数是否可以转换为与第二个操作数匹配。如果两者都可以转换,或者一个可以转换,但转换不明确,则说明程序格式错误。


    段落解释如何 转换为 int :

    5.16/3 关于 condition ? E1 : E2 :

    否则,如果第二个和第三个操作数具有不同的类型,并且其中一个具有(可能是cv限定的)类类型,则尝试将这些操作数中的每一个转换为另一个操作数的类型。如果可以将e1隐式转换为表达式e2在e2转换为右值(如果e2是右值,则转换为它所具有的类型),则可以将e1转换为匹配e2。

    4.3 关于“隐式转换”:

    如果并且仅当声明 T t = e; 对于某些发明的临时变量t来说,它的格式很好。

    8.5/14 关于复制初始化( t t=e; )

    如果源类型是(可能是cv限定的)类类型,则考虑转换函数。列举了适用的转换函数(13.3.1.5),并通过过载分辨率(13.3)选择最佳转换函数。调用如此选定的用户定义的转换以将初始值设定项表达式转换为正在初始化的对象。如果转换无法完成或不明确,则初始化格式错误。

    13.3.1.5 关于转换函数候选者

    考虑了S及其基类的转换函数。那些不隐藏在s和yield类型t中的函数,或者可以通过标准转换序列(13.3.3.1.1)转换为类型t的类型,都是候选函数。

        2
  •  7
  •   David Thornley    15 年前

    在本标准第5.16节中,这一点非常令人困惑。重要部分见第3段。”如果e2是左值:如果e1可以隐式转换(第4条)为“t2的引用”类型,则e1可以转换为匹配e2,但受转换中引用必须直接绑定到e1(8.5.3)的约束。”

    在表达式中,唯一的左值是 item ,因此问题是0(int)是否可以隐式转换为类型 Foo . 在这种情况下,没有将任何其他类型隐式转换为 ,因为只标记了可用的转换函数 explicit . 因此,这不起作用,如果e2是一个右值,或者如果上面的转换不能完成:“(如果两者都有类类型,跳过部分)”否则(即,如果e1或e2有一个非类类型,或者两者都有类类型,但基础类既不相同也不是另一个的一个基类):e1可以是如果可以将e1隐式转换为表达式e2转换为右值时的类型(如果e2是右值,则转换为它的类型),则转换为匹配e1。“

    因此,我们看到0是类型的右值 int . 我们可以转换 ,因为我们可以隐式转换 FOO公司 到A double 从那里到 int . 然后:

    “使用此过程,可以确定第二个操作数是否可以转换为与第三个操作数匹配,以及第三个操作数是否可以转换为与第二个操作数匹配。如果两者都可以转换,则OOR ONE可以转换,但转换不明确,程序格式不正确。如果两者都不能转换,则操作数保持不变,并按照下面的说明执行进一步的检查。如果只能进行一次转换,则该转换将应用于所选操作数,而转换后的操作数将在本节其余部分中代替原始操作数。”

    因为我们可以转换 int ,我们转换 int 在剩下的决定中。我们现在有两个 利息 作为表达式类型,并且至少有一个是右值。

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

    我认为结论是:

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

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

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

        3
  •  1
  •   Ben M    15 年前

    三元表达式的类型在编译时确定;运行时的某些条件无关紧要。

    我想问题是:为什么编译器在第一个示例中选择int而不是double?

        4
  •  0
  •   codymanix    15 年前

    三元运算符根据其参数猜测类型。它无法将项转换为int,但可以将项转换为double,然后将其转换为int。