14

gcc, clang and VS2015 don't elide the call to the move constructor in the code below, after throwing object a. It seems to me the conditions established in bullet point (31.2) of §8.12[class.copy]/31 (N4140) are satisfied.

#include <iostream>

struct A
{
    A() { std::cout << "Default ctor " << '\n'; }
    A(const A& a) { std::cout << "Copy ctor" << '\n'; }
    A(A&& a) { std::cout << "Move ctor" << '\n'; }
    ~A() { std::cout << "Destructor " << '\n'; }
};

int main()
{
    try
    {
        A a;
        throw a;
    }
    catch(A& a) { std::cout << "Caught" << '\n'; }
}

Note that a is an lvalue, but according to §12.8/32, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. That is, the call to the move constructor is OK. If you erase the definition of the move constructor above, the copy constructor is invoked, but again, it is not elided!

I understand the copy-elision is not mandated by the Standard, but I'm curious to know if there is any special condition that could justify the fact, that the three compilers mentioned above avoid this optimization, in this particular example.

An example output for gcc, from the link above:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

Default ctor

Move ctor

Destructor

Caught

Destructor

Community
  • 1
  • 1
Belloc
  • 6,318
  • 3
  • 22
  • 52
  • 1
    it's along the exceptional path, who cares? the equivalent [happy path](http://coliru.stacked-crooked.com/a/44bf1d21b3e185ba) fully optimizes – TemplateRex Sep 27 '15 at 19:48
  • I would think it's about wording that a local variable cannot be elided if its construction has side effects. Not sure if there's really a conflict but as simple rules they might be effectively conflicting for the compiler. – Cheers and hth. - Alf Sep 27 '15 at 19:54
  • 2
    @Cheersandhth.-Alf: Copy-elision is explicitly allowed so that the compiler can do it even is there are side effects. – Dietmar Kühl Sep 27 '15 at 19:55
  • @DietmarKühl: No, I'm not talking about the side effects of the move constructor. – Cheers and hth. - Alf Sep 27 '15 at 19:56
  • @DietmarKühl VS2015 always does copy-elision even when optimizations are disabled. I don't know the details about gcc and clang. But you can verify the compiler switches that were used in gcc, in the link provided above, – Belloc Sep 27 '15 at 19:58
  • @DietmarKühl: No, I mean the side effects of the default constructor here. If there was no throwing the variable could not be elided, even though it's not used, because its construction has side effects. I guess one could test that possibility (rule conflict for the compiler) by removing the side effect. – Cheers and hth. - Alf Sep 27 '15 at 19:58
  • What actually is the output of the program? Just so everyone knows exactly what the sequence is from the text of the question. – Alan Stokes Sep 27 '15 at 20:08
  • @DietmarKühl: well it doesn't matter if you understand what I meant, because that possibility didn't pan out. :( – Cheers and hth. - Alf Sep 27 '15 at 20:09
  • @AlanStokes You'll be able to see the output by clicking the link at the top of the question. I'll repeat it here: http://coliru.stacked-crooked.com/a/19d414d2bc28ba48 – Belloc Sep 27 '15 at 20:14
  • I imagine the answer has to do with how exception objects are stored, allocated, and deallocated. But I don't know enough details about that. – aschepler Sep 27 '15 at 20:21
  • @aschepler: With respect to the optimizations the code is equivalent (roughly) to declaring a second variable `b` copy-initialized from `a`. However, as already discussed, removing the side effect from the default destructor does not elide `a` in this case, with g++. It could do that under the as-if rule. – Cheers and hth. - Alf Sep 27 '15 at 20:23
  • I did some research for clang and came up with [this](http://coliru.stacked-crooked.com/a/a6f14c1527abb404). Not enough for an answer but I hope that at least it makes sense. – Marco A. Sep 28 '15 at 16:04

1 Answers1

3

According to 12.8 [class.copy] paragraph 31, second bullet the copy of a local variable being thrown can be elided:

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 (15.1) can be omitted by constructing the automatic object directly into the exception object

It seems none of the compilers makes use of this optimization. One reason could be that it is simply not worth doing so far as the effort is better spent on other optimizations. I don't think there is anything in the standard prohibiting this optimization.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • One practical reason for not doing it is that this can **reorder side effects**, where something that the local variable's construction does and that following statements before the throw depend on, is moved to the throwing. – Cheers and hth. - Alf Sep 27 '15 at 20:35
  • The relevant standardese for that is in C++11 §3.7.3, stating that the local object (and the side effects of its construction, that following statements may depend on) can be eliminated, "If a variable with automatic storage duration has initialization or a destructor with side effects, it shall not be destroyed before the end of its block, nor shall it be eliminated as an optimization even if it appears to be unused, except that *a class object or its copy/move may be eliminated as specified in 12.8*". – Cheers and hth. - Alf Sep 27 '15 at 20:52
  • Wow, I'm surprised there's a distinct provision for this case. Nice find. – Lightness Races in Orbit Sep 27 '15 at 21:01
  • Defect report [887](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#887) shows that the current wording was adopted due to unsafety of the original. Since there's still extreme unsafety (reordering of side effects) this is arguably a **defect** in the standard. A simple resolution would be to simply ditch the bullet point, since moving can be done explicitly when it's desired. However I don't see any defect report directly about it. – Cheers and hth. - Alf Sep 27 '15 at 21:02