2
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。为什么?

4

4 回答 4

9

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

在第二种情况下,由于操作数具有 type double,因此不会发生这种尾随转换 to int,因此结果类型的?:typedouble具有预期值。

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


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

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


解释您如何Foo转换为的段落int

5.16/3关于condition ? E1 : E2

否则,如果第二个和第三个操作数具有不同的类型,并且其中一个具有(可能是 cv 限定的)类类型,则会尝试将这些操作数中的每一个转换为另一个的类型。[...] 如果 E1 可以隐式转换为表达式 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 的类型是候选函数。

于 2009-09-18T15:36:23.527 回答
7

这在标准的第 5.16 节中有令人困惑的细节。重要的部分在第 3 段中。“如果 E2 是左值:如果 E1 可以隐式转换(第 4 条)为类型“对 T2 的引用”,则可以将 E1 转换为匹配 E2,但要遵守在转换中引用必须直接(8.5.3)绑定到 E1。”

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

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

“使用这个过程,确定第二个操作数是否可以转换为匹配第三个操作数,以及第三个操作数是否可以转换为匹配第二个操作数。如果两者都可以转换,或者一个可以转换但转换是模棱两可,程序格式错误。如果两者都不能转换,则操作数保持不变,并按如下所述执行进一步检查。如果恰好可以进行一次转换,则将该转换应用于所选操作数并使用转换后的操作数代替本节其余部分的原始操作数。”

由于我们可以将 a 转换Foo为 an int,因此我们将 the 转换Foo为 anint以进行其余的确定。我们现在有两个ints 作为表达式类型,并且至少有一个是右值。

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

我认为要点是:

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

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

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

于 2009-09-18T15:57:56.453 回答
1

三元表达式的类型在编译时确定;some_condition 在运行时是什么并不重要。

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

于 2009-09-18T15:31:14.760 回答
0

三元运算符从其参数中猜测类型。它不能将 item 转换为 int,但它可以将 item 转换为 double,然后将其转换为 int。

于 2009-09-18T15:49:42.110 回答