You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

函数中使用引用时的副作用与未定义行为判定

函数中使用引用时的副作用与未定义行为判定

咱们直接切入核心:你给出的这段代码确实属于未定义行为(UB),而非仅仅结果不确定。下面结合C++23标准的条款,一步步拆解这个问题的争议点。

先再贴一遍你的代码方便对照:

int f(int& i) {   // pass by reference
    return i++;  
}

int main() {
    int v=5;  
    auto r=f(v)+ 2*f(v); 
    cout << r <<endl;  
}

关键标准条款与分析

  1. 明确副作用的定义
    正如你引用的C++23/[intro.execution]/7:

    修改对象、调用库IO函数,或者调用执行上述操作的函数,都属于副作用——即会改变执行环境状态的操作。

    这里f(v)的调用会通过引用修改v的值(i++操作直接作用于绑定的v),所以每个f(v)调用都带有对v的副作用,这点是没有争议的。

  2. 操作数的求值顺序规则
    根据C++23/[intro.execution]/10:

    除非标准特别说明,单个运算符的操作数、单个表达式的子表达式的求值都是**未排序(unsequenced)**的。

    回到你的代码:f(v) + 2*f(v)是一个加法表达式,它的两个操作数分别是左侧的f(v)和右侧的2*f(v)。这两个操作数的求值是完全未排序的——标准没有规定哪一个必须先执行。
    同时,右侧乘法表达式2*f(v)的两个操作数(常量2和f(v))的求值也是未排序的,这意味着第二个f(v)的调用时机和左侧的f(v)没有任何顺序约束。

  3. 未排序副作用触发UB的条件
    同样是[intro.execution]/10的核心规则:

    如果对同一个内存位置的副作用,相对于另一个对该位置的副作用,或者相对于对该位置的值计算,是未排序的,且不属于潜在并发操作,那么行为就是未定义的。

回应争议点:为什么“函数体执行是不确定排序”的说法不成立?

首先要区分两个关键概念:

  • 不确定排序(indeterminately sequenced):两个操作必然有先后顺序,但具体谁先谁后未指定(比如函数多个参数的初始化过程就是这种关系)。这种情况下,副作用不会重叠,只会有两种确定的结果,不会触发UB。
  • 未排序(unsequenced):两个操作没有任何顺序约束,语义上允许“逻辑交错”(单线程实际执行不会真的交错,但标准不保证任何顺序)。

你的代码中,两个f(v)的调用是作为未排序的操作数存在的,它们的整个求值过程(包括函数体中修改v的副作用)是完全未排序的——标准没有要求它们必须有先后顺序。这种情况下,两个对v的修改属于未排序的副作用,完全符合UB的判定条件。

简单来说:这段代码的问题不是“结果要么是17要么是16”,而是编译器可以生成任何行为——比如输出随机值、直接崩溃,甚至优化掉整个表达式,这些都在标准允许的范围内。

备注:内容来源于stack exchange,提问作者Christophe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:18:11