在阅读cppreference的复制省略之后,我想玩例外和复制省略。 所以我在coliru
上用gcc 7.2编写了以下代码#include <iostream>
class Exception
{
public:
Exception() { std::cout << "constructed\n"; }
Exception(Exception& other) {std::cout << "copy constructed\n";}
Exception(const Exception& other) {std::cout << "copy constructed\n";}
Exception(Exception&& other) {std::cout << "move constructed\n";}
};
void foo()
{
throw Exception();
}
int main()
{
try
{
foo();
}
catch(Exception e )
{
}
}
输出
构造
复制构造
我们可以看到复制构造函数被调用,即使用-O2调用gcc也会发生这种情况。 在我看来,根据以下条款,此代码应该有资格复制elision:
处理异常时,如果catch子句的参数是 相同类型(忽略顶级cv资格)作为例外 抛出的对象,省略了副本和catch子句的主体 直接访问异常对象,就像通过引用捕获一样。
那为什么复制构造函数被调用?为什么复制省略在这种情况下不起作用?
答案 0 :(得分:5)
cppreference对此不准确。事实上,除了常量表达式(constexpr
)或常量初始化(静态或线程本地)之外,实际上无法保证副本将被省略。在你的例子中情况并非如此。
请参阅当前C ++ 17草案中的[class.copy.elision]/1(强调我的):
当满足某些条件时,......在以下情况下,复制/移动操作的省略,称为复制省略, 允许 :
- 在 throw-expression 中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会延伸到超出最里面的封闭try-block(如果有的话),通过将自动对象直接构造到异常对象中,可以省略从操作数到异常对象的复制/移动操作
复制elision是 必需 ,其中表达式在需要常量表达式和常量初始化的上下文中进行评估>
这意味着有一天它可能被实现为编译器优化,但目前情况并非如此。
因此暂时catch
const&
是明智的(也是为了避免意外切片)。