C++运算符优先级是编译期由语法规则硬编码决定的,非运行时查表;+和等内置运算符的结合顺序在语法树中已固定为a+(bc),自定义重载也复用该规则。

为什么 + 和 * 不用你写优先级也能算对
因为 C++ 编译器根本不管你的“想法”,它只认语法树。表达式 a + b * c 在词法分析和语法分析阶段就被构造成 a + (b * c) 的树形结构,* 节点天然挂在 + 的右操作数上——优先级不是运行时查表决定的,是编译期硬编码在语法规则里的。
所以你没法“改”内置运算符的优先级,也不该试图绕过它去手动加括号模拟;真有歧义,说明表达式本身可读性已出问题。
- 自定义类重载
operator+或operator*时,它们的结合性、优先级完全复用内置规则,不会因为你写了MyNum operator*(...)就变低 - 宏定义里用
#define MUL(a,b) a*b然后写MUL(x+1, y+2)→ 展开成x+1*y+2,这是宏的坑,不是优先级的锅 - 模板元编程或 constexpr 表达式里,照样遵守同一套文法,
value中的+和*仍是编译期按标准优先级求值
operator[] 和 operator() 为什么能“插队”到高优先级位置
不是它们优先级高,是它们根本不在传统“二元运算符优先级表”里参与排序。operator[]、operator()、operator-> 这些后缀形式属于“后缀表达式(postfix expression)”,语法层级比 +、&& 这类“二元表达式(binary expression)”更高——就像函数调用 f() 总是先于外面的 + g() 执行,这不是优先级数字大,是语法结构决定了它必须先被完整解析。
-
a[0] + b[1]:两个[]各自先完成下标访问,再做+,不是因为[]优先级数值比+大,而是文法规定postfix-expression是additive-expression的基础构成单元 -
v.at(i)++:后缀++绑定在v.at(i)整体上,不是i上;因为function-call属于postfix-expression,而++(后缀)也属于同一层,左结合,所以合法 - 如果你重载了
operator[]返回int&,那a[0] = 5中的赋值其实是*(a.operator[](0)) = 5,=的优先级此时作用在解引用结果上
手写表达式解析器时,别碰“优先级数字表”
真要解析类似 3 + 4 * 5 - 2 的字符串,用递归下降(recursive descent)直接对应文法规则,比维护一个 map<token int></token> 优先级表更稳、更易调试。C++ 标准里那个“17 级优先级表”是给编译器作者看的,不是给你 runtime 解析用的说明书。
立即学习“C++免费学习笔记(深入)”;
- 错误做法:写个
get_precedence(op),再搞个运算符栈,手动模拟调度场算法——容易漏掉结合性(如a = b = c是右结合)、括号嵌套、一元负号等边界 - 正确路径:定义
parse_expression(min_prec = 0),内部按 precedence threshold 分层调用parse_additive()→parse_multiplicative()→parse_primary(),每层只处理本级允许的运算符 - 示例片段:
parse_multiplicative()会先调parse_primary()拿左操作数,然后循环检查下一个 token 是否为*或/,是则继续 parse_primary 取右操作数并构造节点——自然体现*比+“粘得更紧”
模板推导和 decltype 里的表达式,优先级照常生效
decltype 不是“取类型快照”,它会对表达式做完整语义分析,包括优先级解析。写 decltype(a + b * c),编译器真会按 b * c 先算、再加 a 的逻辑走一遍,最后取这个“结果表达式”的类型。
-
int a = 1; char b = 2; short c = 3;,则decltype(a + b * c)是int,因为b * c提升为int,再与a相加仍是int - 但
decltype((a + b * c))带额外括号 → 整个是“纯左值表达式”,结果类型是int&,括号改变的是值类别(value category),不是优先级 - 模板参数推导中,
template<typename t> void f(T&&); f(a + b * c);</typename>推出的T是int,同样基于优先级解析后的表达式类型
最易忽略的一点:优先级影响的不只是计算顺序,它还决定哪些子表达式会被当作整体参与类型推导、SFINAE 或概念约束。少一个括号,可能就从 int 变成 int&,进而让重载决议或 requires 检查静默失败。











