如何确定三元运算符的返回类型?

How is the return type of a ternary operator determined?

本文关键字:运算符 返回类型 三元 何确定      更新时间:2023-10-16

我正在解决一个关于主教在棋盘上移动的问题。在我的代码的某一点,我有以下语句:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

这将生成以下错误:

error: invalid operands of types 'int' and '<unresolved overloaded function type>' to binary 'operator<<'

但是,我通过在代码中包含附加变量来立即修复此错误:

int steps = (abs(c2-c1) == abs(r2-r1)) ? 1 : 2;
std::cout << steps << std::endl;

三元运算符如何工作,其返回类型如何确定(编译器称之为<unresolved overloaded function type>(?

这与如何推导返回类型无关,而与运算符优先级有关。 当你有

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;

它不是

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

因为?:的优先级低于<<. 这意味着你实际拥有的是

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

这就是为什么你会得到一个关于<unresolved overloaded function type>的错误。 只需使用括号,例如

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

你会没事的。

你必须在三元运算两边加上括号:

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;

如果不是,<<运算符将转到2并给出错误,因为它没有这样的重载函数。

这是因为按位左移运算符(<<(的优先级高于三元运算符。您可以在C++参考的此页面中查看运算符的完整列表及其优先级。

由于运算符优先级,该行被视为:

(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);

将其更改为

std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;
//           ^----------------------------------^
//           Surrounding parentheses

当解析顺序可视化时,很容易看到错误:

std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;
_______/                                                      <--- #1
________________________/   V   ~~~~error~~~/   <--- #2
_____________________________________________/   <--- #3
__________________________________________________________/   <--- #4
___________________________________________________________/  <--- #5

你提出的问题的字面答案是C++语言标准 [expr.cond] 部分中的算法。

基本规则"确定是否可以从第二个操作数到为第三个操作数确定的目标类型形成隐式转换序列,反之亦然。 如果没有一个可能的转换,或者有多个转换,则为语法错误,但有几种特殊情况(此处不适用(:

  • 如果两者都是算术或enum类型,您将获得相同的p ? a : b隐式转换,以确定表达式的类型,例如a + ba * b
  • 其中一个目标可能是throw表达式,并被视为具有另一个目标的类型。
  • 如果其中一个目标是位域,则条件表达式的类型也是如此
  • 具有不同限定符(如constvolatile(的指针的限定符是统一的。

如果目标是相同类型的 gl值,则结果为 glvalue,否则为 prvalue。

如有疑问,您始终可以显式强制转换一个或两个操作数,以便它们具有相同的类型。

正如公认的答案所解释的那样,您在此处的实际问题是运算符优先级。 也就是说,编译器将第三个操作数解析为2 << std::endl,而不是 2。

根据 cpp 偏好:

解析表达式时,与在其下方优先级较低的行上

列出的任何运算符相比,在上表的某行中列出并具有优先级的运算符将更紧密地绑定到其参数(就像用括号一样(。例如,表达式std::cout << a & b*p++被解析为(std::cout << a) & b*(p++),而不是std::cout << (a & b)(*p)++

具有相同优先级的运算符在其关联性方向上受其参数的约束。例如,表达式a = b = c被解析为a = (b = c),而不是因为赋值的从右到左结合性而被解析为(a = b) = c,但a + b - c被解析为(a + b) - c而不是a + (b - c)因为加法和减法的从左到右结合性。

关联性规范对于一元运算符是多余的,并且仅出于完整性而显示:一元前缀运算符始终从右到左关联(delete ++*pdelete (++(*p))),一元后缀运算符始终从左到右关联(a[1][2]++((a[1])[2])++(。请注意,关联性对于成员访问运算符是有意义的,即使它们与一元后缀运算符分组:a.b++(a.b)++解析而不是a.(b++)

运算符优先级不受运算符重载的影响。例如,std::cout << a ? b : c;解析为(std::cout << a) ? b : c;因为算术左移的优先级高于条件运算符。