Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Throw temporary instead of local variable - why?

Tags:

c++

The code in question is this:

struct something_bad_happened_exception : std::exception {};

void foo() {
    something_bad_happened_exception e;
    throw e;
}

clang gives a warning that reads:

Throw expression should throw anonymous temporary values instead [cert-err09-cpp]

which means foo() should be changed to:

void foo() {
    throw something_bad_happened_exception();
}

Why is it better to throw a temporary instead of a local variable?

like image 480
alain Avatar asked Nov 21 '18 12:11

alain


2 Answers

According to the cpp reference on a throw expression:

First, copy-initializes the exception object from expression (this may call the move constructor for rvalue expression, and the copy/move may be subject to copy elision)

Thus, your code if perfectly fine, but a copy constructor will be called, which may be undesirable in terms of efficiency. Still, copy elision may occur if (emphasis mine)

Under the following circumstances, the compilers are permitted, but not required to omit the copy

[omissis]

In a throw-expression, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and whose scope does not extend past the innermost try-block (if there is a try-block).

As an example, consider the following code

#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;
}

Compiling the code with gcc 8.2.1 and clang 6.0, with option -O3 the output is

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

The first throw corresponds to your example. Even if a copy of e could be omitted, neither gcc nor clang implement copy elision.

The second throw has an anonymous temporary, and no copy occurs.

like image 136
francesco Avatar answered Nov 06 '22 00:11

francesco


The code doesn't conform to the convention of "throw only anonymous temporaries" (note the option CheckThrowTemporaries), which is what the style checker inspects.

That convention, and this inspection are overly strict as far "throw by value" convention is concerned - in that regard the example program is conforming. A truly non conforming example would be throwing a pointer to an object.

like image 42
eerorika Avatar answered Nov 06 '22 01:11

eerorika