有问题的代码是这样的:
struct something_bad_happened_exception : std::exception {};
void foo() {
something_bad_happened_exception e;
throw e;
}
c发出警告,内容为:
引发表达式应该改为引发匿名临时值 [cert-err09-cpp]
这意味着foo()
应该更改为:
void foo() {
throw something_bad_happened_exception();
}
为什么最好抛出一个临时变量而不是一个局部变量?
答案 0 :(得分:6)
根据throw expression上的cpp参考:
首先,从表达式中复制初始化异常对象(这可能会调用rvalue表达式的move构造函数,并且复制/移动可能会受到复制省略的影响)
因此,您的代码就算不错,但是将调用复制构造函数,这在效率方面可能是不可取的。不过,copy elision如果(强调我的话)可能会发生
在以下情况下,允许编译器,但并非必需,以省略副本
[催眠]
在throw-expression中,当操作数是具有自动存储持续时间的非易失性对象的名称时,它不是函数参数或catch子句参数,并且其作用域不超出最深的try-块(如果有尝试块)。
作为示例,请考虑以下代码
#include <exception>
#include <iostream>
struct something_bad_happened_exception : std::exception {
something_bad_happened_exception(const something_bad_happened_exception& r) {
std::cout << "A copy has occoured!" << std::endl;
}
something_bad_happened_exception() { }
};
int main()
{
std::cout << "First throw" << std::endl;
try {
const something_bad_happened_exception e;
throw e;
}
catch (const std::exception& ex)
{
std::cout << "Caught exception" << std::endl;
}
std::cout << "Second throw" << std::endl;
try {
throw something_bad_happened_exception();
}
catch (const std::exception& ex)
{
std::cout << "Caught exception" << std::endl;
}
return 0;
}
使用gcc 8.2.1
和clang 6.0
编译代码,使用选项-O3
,输出为
First throw
A copy has occoured!
Caught exception
Second throw
Caught exception
第一个throw
与您的示例相对应。即使可以省略e
的副本,gcc
和clang
都不会实现复制省略。
第二个throw
有一个匿名临时文件,没有副本发生。
答案 1 :(得分:4)
该代码不符合样式检查器检查的约定“ throw only anonymous temporaries”(请注意选项CheckThrowTemporaries
)。
就“按价值抛出”惯例而言,该惯例和该检查都过于严格-在这方面,示例程序是合规的。一个真正不符合标准的示例就是将指针抛出一个对象。