【发布时间】:2021-12-14 03:15:25
【问题描述】:
我有以下代码:
#include <vector>
#include <iostream>
std::vector <int> a;
int append(){
a.emplace_back(0);
return 10;
}
int main(){
a = {0};
a[0] = append();
std::cout << a[0] << '\n';
return 0;
}
函数append() 作为副作用将向量大小增加一。由于向量的工作方式,这可能会在超出其容量时触发其内存的重新分配。
因此,在执行a[0] = append() 时,如果发生重新分配,则a[0] 无效并指向向量的旧内存。因此,您可以预期该向量最终将是 {0, 0} 而不是 {10, 0},因为它分配给旧的 a[0] 而不是新的。
让我感到困惑的是,这种行为在 C++14 和 C++17 之间发生了变化。
在 C++14 上,程序将打印 0。在 C++17 上,它将打印 10,这意味着 a[0] 实际上分配了 10。所以,我有以下问题我找不到答案:
- C++17 是否在评估赋值表达式的 RHS 后评估
a[0]的内存地址? C++14 之前是否评估过这个,这就是它改变的原因? - 这是在 C++17 中修复的错误吗?标准有何变化?
- 在使用 C++14 或 C++11 时,是否有一种简洁的方法可以使此赋值的行为类似于 C++17 中的行为?
【问题讨论】:
-
Because of this you can expect,因为它是 UB,你不能指望任何特定的行为。 -
它是未定义的,或者至少是未指定的,但这是 C++17 中的重大变化。
-
@BlazKorecic 如果首先评估子表达式
a[0],则结果为 UB,因为它是一个悬空引用并且您正在分配给它。在 C++17 之前,子表达式的求值顺序在很大程度上未指定,因此无法保证a[0]会在append()之前求值,但如果是,那么由于悬空引用,您的程序具有 UB。 C++17 将赋值运算符的求值顺序规则更改为从右到左,因此 C++17 保证append()在a[0]之前求值,因此没有悬空引用和 UB。跨度> -
无论如何,你永远不想像你的例子那样编写代码,因为它不直观。像
a[0] = append();这样的代码没有理由存在于生产代码中。您的append函数不遵守 SRP(单一责任原则),因为它做了两件事(附加数据,返回任意值)。
标签: c++ c++17 c++14 stdvector assignment-operator