Copy elision and exceptions
Asked Answered
D

2

6

After reading about copy elision of cppreference I wanted to play with exceptions and copy elision. So I wrote the following code with gcc 7.2 on coliru

#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 )
    {

    }
}

Output

constructed
copy constructed

We can see that the copy constructor is called and this happens even when gcc is invoked with -O2. It seems to me that this code should be eligible to copy elision according to the following clause :

When handling an exception, if the argument of the catch clause is of the same type (ignoring top-level cv-qualification) as the exception object thrown, the copy is omitted and the body of the catch clause accesses the exception object directly, as if caught by reference.

So why is the copy constructor called? Why does copy elision not work in this case?

Dennie answered 15/1, 2018 at 8:20 Comment(0)
M
5

cppreference is inaccurate on this. There is in fact no guarantee that the copy will be elided except in a constant expression (constexpr) or a constant initialization (static or thread-local). That is not the case in your example.

See [class.copy.elision]/1 in the current C++17 draft (emphasis mine):

When certain criteria are met, ... elision of copy/move operations, called copy elision, is permitted in the following circumstances:

— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object can be omitted by constructing the automatic object directly into the exception object

Copy elision is required where an expression is evaluated in a context requiring a constant expression and in constant initialization.

That means that some day it may be implemented as a compiler optimization, it is just not the case currently.

It would be wise therefore to catch by const& for the time being (also to avoid accidental slicing).

Mercia answered 15/1, 2018 at 10:26 Comment(0)
R
0

Just, the elision your compiler did not perform for you when catching the exception is non-mandatory. It is only permitted but not obligated to perform the elision.

Note, the elision your compiler did perform for you when throwing is non-mandatory too.

And it is how it is described in https://en.cppreference.com/w/cpp/language/copy_elision at present.

Of course, you can avoid this calling of the copy constructor, catch the exception by reference, either

catch (const Exception & e)

or

catch (Exception & e)

(according to your needs).

Ranice answered 24/8, 2021 at 16:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.