基本类型变量的赋值计算序列,右操作数引发异常

Computation sequence of assignment, for a variable of fundamental type, with right operand throwing an exception

本文关键字:操作数 异常 类型变量 赋值 计算      更新时间:2023-10-16

如果我使用一个可能抛出的表达式来为基本类型的变量赋值,那么如果抛出异常,是否可以保证变量的状态不变?

例如,在下面的代码中,如果我们选择"选项A",并且operator new()抛出异常,那么在堆栈展开期间调用析构函数时,成员变量ptr是否有机会不等于nullptr

#include <new>  // operator new   operator delete
class MemoryHolder {
private:
void * ptr;
public:
MemoryHolder () {
ptr = nullptr;
}
void increase_memory () {
operator delete (ptr);
ptr = nullptr;
// Option A (unsafe?)
ptr = operator new (10);
// Option B (safe)
void * new_ptr = operator new (10);
ptr = new_ptr;
}
~MemoryHolder () {
operator delete (ptr);
}
};

我很想知道C++14和C++17的答案。

我的猜测是:"选项A"在C++14和C++17中是安全的,因为它们都包含此语句(参见§[expr.ass]¶1):

在所有情况下,赋值都是在左右操作数的值计算之后排序的

但是,我不确定执行左操作数的"值计算"是否不包括给它一个新值。我在标准中没有找到"价值计算"的定义。

相关(但我不清楚):C++中的赋值发生,尽管右侧出现异常

在下面的代码中,如果我们选择"选项A",并且operator new()抛出异常,那么在堆栈展开过程中调用析构函数时,成员变量ptr是否有机会不等于nullptr

我会暂时忘记,ptrnew调用之前从未在MemoryHolder中实际初始化,因此可能具有不确定的值。因此,您的问题是"ptr在分配之前和分配之后的值相同吗?">

在某种程度上,你可以实际验证这一点(在构造函数中,这意味着你必须在构造函数内部捕获异常),是的,它将具有相同的值。

我在标准中没有找到"值计算"的定义。

虽然该标准有时会深入研究古怪的语言,但它并不是试图主动欺骗你。如果一个术语没有定义,那么它应该按面值计算。"价值计算"是指。。。计算该值。你把A分配给B。这意味着要弄清楚A和B都是什么。这包括为它们计算值,然后执行赋值。

相关(但我不清楚):C++中的赋值发生,尽管右侧出现异常

这与您的问题无关,因为这是关于赋值两边的"值计算"顺序,而不是关于执行赋值。那个问题的OP弄错了。任务从来没有真正发生过。只是map::operator[]将在映射中创建一个条目(如果还不存在的话)。由于C++14不会对赋值操作的两侧进行排序,因此实现可以允许它们以任何顺序发生。在这种情况下,operator[]首先发生,因此即使没有发生赋值,也会插入一个元素。