三元运算符的返回类型是如何确定的? [复制]
Posted
技术标签:
【中文标题】三元运算符的返回类型是如何确定的? [复制]【英文标题】:How is the return type of a ternary operator determined? [duplicate] 【发布时间】:2019-12-15 11:21:05 【问题描述】:我正在解决一个关于象棋棋盘上移动的问题。在我的代码中,我有以下语句:
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>
)?
【问题讨论】:
我认为三元运算符优先于 查看运算符优先级。 en.cppreference.com/w/cpp/language/operator_precedence) 这也修复了错误:std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;
。不需要中间变量。
这个问题是由移位运算符的再利用引起的 - int x = whatever ? 1 << 2 : 1 << 3;
具有“明显”的含义。
@Neo:为了澄清事实,既然问题进入了 HNQ,那么您的赞成评论已经获得了更多的关注——事实上,第三个论点最终是 2 << std::endl
而不是比2
意味着<<
运算符优先于三元运算符,而不是相反。我确定这就是你的意思,但由于某种原因,结果却相反。
【参考方案1】:
您必须在三元运算周围加上括号:
std::cout << ((abs(c2-c1) == abs(r2-r1)) ? 1 : 2) << std::endl;
如果不是 <<
运算符转到 2
并给出错误,因为它没有这样的重载函数。
发生这种情况是因为按位左移运算符 (<<
) 的优先级高于三元运算符。您可以在this page of the C++ reference 中查看运算符的完整列表及其优先级。
【讨论】:
【参考方案2】:根据cppreference:
解析表达式时,在上表某行中列出的具有优先级的运算符将比在其下方的行中列出的任何运算符更紧密地绑定到其参数(就像用括号一样)较低的优先级。例如,表达式
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 ++*p
是delete (++(*p)))
,一元后缀运算符总是从左到右关联(a[1][2]++
是((a[1])[2])++
)。请注意,关联性对于成员访问运算符是有意义的,即使它们与一元后缀运算符分组:a.b++
被解析为(a.b)++
而不是a.(b++)
。运算符优先级不受运算符重载的影响。例如
std::cout << a ? b : c;
解析为(std::cout << a) ? b : c;
,因为算术左移的优先级高于条件运算符。
【讨论】:
【参考方案3】:解析顺序可视化时很容易看出错误:
std::cout << (abs(c2-c1) == abs(r2-r1)) ? 1 : 2 << std::endl;
\_______/ <--- #1
\________________________/ V \~~~~error~~~/ <--- #2
\_____________________________________________/ <--- #3
\__________________________________________________________/ <--- #4
\___________________________________________________________/ <--- #5
【讨论】:
【参考方案4】:由于operator precedence,该行被视为:
(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
【讨论】:
【参考方案5】:这与返回类型如何推导无关,与operator precedence 无关。当你有
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;
你会没事的。
【讨论】:
所以我的猜测是:编译器试图将这部分1 : 2 << std::endl
二进制化,但失败了。
@MerajalMaksud 是的。基本上它会返回的两件事变成了1
和(2 << std::endl)
@MerajalMaksud 差不多,但不完全。它试图编译2 << std::endl
,但失败了。
有趣的是,std::cout << 2 - (abs(c2-c1) == abs(r2-r1)) << std::endl;
相当于固定代码。
原来的代码实际上被解析为(std::cout << (abs(c2-c1) == abs(r2-r1))) ? 1 : (2 << std::endl);
“幸运”,iostreams可以转换为布尔表达式,即第一部分不会导致错误(只是意外行为)。【参考方案6】:
你问的问题的字面答案是[expr.cond] secton of the C++ language standard.中的算法
基本规则“确定是否可以从第二个操作数到为第三个操作数确定的目标类型形成隐式转换序列,反之亦然。”如果没有一种可能的转换,或者不止一种,则为语法错误,但有几种特殊情况(此处不适用):
如果两者都是算术类型或enum
类型,您将获得相同类型的 p ? a : b
隐式转换,它确定表达式的类型,例如 a + b
或 a * b
。
其中一个目标可能是throw
表达式,并被视为具有另一个目标的类型。
如果目标之一是位域,那么条件表达式的类型也是如此
具有不同限定符(如const
和volatile
)的指针具有统一的限定符。
如果目标是相同类型的glvalue,则结果为glvalue,否则为prvalue。
如有疑问,您始终可以显式转换一个或两个操作数,以使它们具有相同的类型。
正如接受的答案所解释的那样,您的实际问题是运算符优先级。也就是说,编译器将第三个操作数解析为2 << std::endl
,而不是2。
【讨论】:
以上是关于三元运算符的返回类型是如何确定的? [复制]的主要内容,如果未能解决你的问题,请参考以下文章