抛出临时变量而不是局部变量-为什么?

时间:2018-11-21 12:50:26

标签: c++

有问题的代码是这样的:

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();
}

为什么最好抛出一个临时变量而不是一个局部变量?

2 个答案:

答案 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.1clang 6.0编译代码,使用选项-O3,输出为

First throw
A copy has occoured!
Caught exception
Second throw
Caught exception

第一个throw与您的示例相对应。即使可以省略e 的副本,gccclang都不会实现复制省略。

第二个throw有一个匿名临时文件,没有副本发生。

答案 1 :(得分:4)

该代码不符合样式检查器检查的约定“ throw only anonymous temporaries”(请注意选项CheckThrowTemporaries)。

就“按价值抛出”惯例而言,该惯例和该检查都过于严格-在这方面,示例程序是合规的。一个真正不符合标准的示例就是将指针抛出一个对象。

相关问题