Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is public destructor necessary for mandatory RVO in C++?

Please consider the simple example as follows, where the function bar returns an object of class A with private destructor, and mandatory return value optimization (RVO) must take place:

class A { ~A() = default; };
A bar() { return {}; }

The code is accepted by Clang, but rejected by GCC with the error:

error: 'constexpr A::~A()' is private within this context
    2 | A bar() { return {}; }
      |                   ^

https://gcc.godbolt.org/z/q6c33absK

Which one of the compilers is right here?

like image 363
Fedor Avatar asked Aug 04 '21 20:08

Fedor


People also ask

Why are constructor and destructor necessary?

The constructor is used to allocate the memory if required and constructing the object of class whereas, a destructor is used to perform required clean-up when an object is destroyed. The destructor is called automatically by the compiler when an object gets destroyed.

Is destructor mandatory in C++?

If no user-defined destructor exists for a class and one is needed, the compiler implicitly declares a destructor.


Video Answer


2 Answers

This is CWG 2426. The destructor is potentially invoked within this context, because even after the initialization of the return A object, it's still possible that the function fails to complete successfully: any temporaries created during the return statement, and automatic local variables that are in scope, must be destroyed, and if the destruction throws, then as part of stack unwinding, the A object is destroyed. Compilers should require the destructor to be accessible at this point.

Note 1: exceptions thrown by the destructors of local variables in the outermost scope of the function can be caught by a function try block.

Note 2: after the return object is destroyed, the handler is allowed to execute another return statement. There is an example of this in the standard.

like image 133
Brian Bi Avatar answered Oct 19 '22 18:10

Brian Bi


There are many simple cases like in the question where it can be easily proven that the destructor will never be used, however the code is used.

It can get arbitrarily complex to decide that question though, which is the bane of standardization. And leaving it in the hands of the implementers will thus splinter the language, creating incompatible sub-dialects as they go to varying amounts of effort to decide (different) corner-cases.

But even that isn't the end of this, as solving the problem means solving the halting-problem, and thus isn't even intractable, but undecidable.

Thus, side-stepping it as CWG 2426 does is not only for sanity (specifying all the details gets unwieldy extremely fast), but the only choice without capriciously drawing a line after dictating any number of arbitrarily chosen simple cases.

like image 25
Deduplicator Avatar answered Oct 19 '22 20:10

Deduplicator