在C ++中,可以重新排序两个throw表达式或throw表达式周围的表达式吗?
例如,在以下代码中:
int f(int x) {
if (! (x >= 0))
throw std::range_error("argument must be non-negative");
return pure_sqrt(x);
}
inf g(int y) {
if (! (y > 0))
throw std::range_error("argument must be positive");
return pure_invsqrt(y);
}
void test(int x, int y) {
try {
int a = f(x);
int b = g(y);
return a + b;
} catch (std::exception e) {
std::cout << "Error: " << e.what() << "." << std::endl;
}
}
假设x == -1
和y == -1
都可以在g
的异常之前抛出f
(“参数必须为正”)的异常?
也就是说,可以对纯函数(除了它们的抛出行为)进行重新排序,还是将未被捕获的异常视为try
块的其余部分的可观察行为?
答案 0 :(得分:2)
当在try
块中抛出异常时,执行会立即跳转到调用堆栈上最接近的匹配catch
语句,并且不会执行{{1}中的其余代码阻止。如果您要同时检查try
和f
两个函数,则需要将它们放在不同的g
块中。
答案 1 :(得分:2)
没有。当f抛出时,catch块中继续执行。 g永远不会被执行。
答案 2 :(得分:1)
所以对于这个问题,“编译器是否可以重新排序抛出表达式或表达式周围的表达式?”答案是,这取决于可观察行为这些表达式的原因。
编译器必须生成一个程序集,该程序集遵循通常名为“就像规则”的规则。为简化起见,此规则规定编译器不能以改变其可观察行为的方式更改代码的含义。这个c ++标准section解释了这一点(阅读整章,是一个很好的建议)。 可观察行为定义如下:
根据抽象机器的规则严格评估通过volatile glvalues的访问。
在程序终止时,写入文件的所有数据应与根据抽象语义生成的程序执行的可能结果之一相同。
交互式设备的输入和输出动态应以在程序等待输入之前提示输出实际传送的方式进行。 构成交互设备的是实现定义。
在代码示例中,在f
或g
执行期间抛出的异常将被catch子句捕获,然后异常消息被输出。如果编译器正在重新排序对f
和g
的调用,它将改变代码的“可观察行为”。
但是编译器或CPU仍然可以对throw表达式周围的其他指令重新排序,只要这不会改变可观察行为。例如,挤出可以按以下顺序运行:
int test(int x, int y) {
//I made some typo fix to your code assuming that test should return int or rethrow.
bool f_precondition = (x >= 0);
bool g_precondition = (y > 0);
try {
if (! f_precondition){
throw std::range_error("argument must be non-negative");
if (! g_precondition)
throw std::range_error("argument must be positive");
} catch (std::exception e) {
std::cout << "Error: " << e.what() << "." << std::endl;
throw;
}
return pure_sqrt(x) + pure_invsqrt(y);
}
智能编译器也可以忽略throw - &gt;抓住路径;
在可观察行为列表中,注意读取或写入原子变量的绝对值。
答案 3 :(得分:0)
不,它们不能重新排序,因为标准没有明确允许这样做,并且as-if规则不能在这里应用,因为它会改变程序的可观察行为。
例如,通过检查x
和y
值,您可以确定要抛出哪个 异常,并通过检查异常对象,您可以确定哪个异常被抛出。