为啥我分配对象本身不能正常工作?

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++ 标准上它是否是未定义的行为(是否已排序)感到困惑。非常感谢您的解释。

以上是关于为啥我分配对象本身不能正常工作?的主要内容,如果未能解决你的问题,请参考以下文章

为啥必须动态分配扩展数组才能使此函数正常工作 C++

为啥我不能将 new 创建的 &client 对象分配给 unique_ptr

为啥 AsyncTask 不能正常工作?

为啥我的 C 程序不能正常工作?

为啥我的约束不能正常工作?

为啥我的多通道映射不能正常工作?