为啥我分配对象本身不能正常工作?
Posted
技术标签:
【中文标题】为啥我分配对象本身不能正常工作?【英文标题】:Why my assigning an object itself doesn't work properly?为什么我分配对象本身不能正常工作? 【发布时间】:2021-11-23 11:49:08 【问题描述】:AFAIK,对于不保证其操作数的求值顺序的运算符,我们不应多次修改操作数。
这里有我写的这个 sn-p:
我正在使用 gcc 和 C++20 标准对其进行编译:-std=c++2b
。
int main()
std::string s = "hello";
auto beg = s.begin();
*beg = *beg++;
std::cout << s << " " << *beg << std::endl;
为什么我会得到这个输出?
hhllo h
这个:
*beg = (*beg)++;
std::cout << s << " " << *beg << '\n';
产生这个输出:
hello h
为什么'h'
的值没有增加到'i'
?
这里发生了什么?通常,这会将s
中的第一个字符分配回自身,然后递增beg
迭代器。但是迭代器并没有递增,而是值发生了变化!我敢打赌这是因为未定义的行为,但如果是这样,那么有人可以向我解释一下吗?
C++17 标准是否将赋值运算符添加到“序列化”运算符中?如果是这样,为什么它在我的示例中不能正常工作?
【问题讨论】:
为什么重要?没有人应该编写那种代码。 是的,赋值在 C++17 中排序:en.cppreference.com/w/cpp/language/eval_order。 (如果为 ≥C++17 编译,这意味着没有 UB。) 你为什么期待不同的结果呢? C++17 中的变化状态是首先评估右侧,因此左侧的beg
指向第二个字符
你真的需要更具体。您使用 C++20 构建的如此重要的细节应该在问题本身中提到。甚至可以作为标签添加。
是的,它是安全的,尽管有用性值得怀疑,因为您自己不知道您自己的代码会发生什么行为(尽管第二个代码 sn-p 似乎更多地混淆了后缀递增回报)
【参考方案1】:
所有这些结果都是正确的。您的误解来自不知道后缀增量是如何工作的。
*beg = *beg++;
根据 C++ 运算符优先级的规则,后缀运算符首先出现。所以这是*beg = *(beg++)
。
C++17 强制赋值运算符在计算左侧之前完全计算右侧的所有表达式,包括所有副作用。
后缀迭代器增量增加左值,但它返回原始值的副本。原始值是指向第一个字符的迭代器。所以在评估beg++
之后,beg
将改变它的位置(因此指向第二个字符)。但是表达式的值是一个指向第一个的迭代器。
然后这个迭代器被取消引用,从而返回对第一个字符的引用。
之后,评估 lhs。 beg
如前所述,指向第二个字符。所以*beg
是对第二个字符的引用,它被分配给第一个字符的值。
*beg = (*beg)++;
所以这是相反的。 *beg
是对第一个字符的引用,然后后缀递增。这意味着*beg
引用的对象是递增的,但表达式的值 是原始值 的副本。即,增加之前的字符:'h'
。
然后您将此字符分配给第一个字符,从而覆盖您刚刚增加的内容。
顺便说一句:如果您必须深入了解这个细节才能弄清楚表达式在做什么,那么您不应该那样写表达式。避免使用后缀表达式,除非它们在某些方面是绝对必要的。
【讨论】:
相信我,我确实知道后缀递增/递减运算符是如何工作的,但是在不同的 C++ 标准上它是否是未定义的行为(是否已排序)感到困惑。非常感谢您的解释。以上是关于为啥我分配对象本身不能正常工作?的主要内容,如果未能解决你的问题,请参考以下文章