51

I have the following example code:

#include <iostream>
using namespace std;

struct foo {
    foo()  { cout << "foo constructed.\n"; }
    ~foo() { cout << "foo destroyed.\n"; }
};

struct bar {
    bar(foo t=foo{}) { }
};

int main(int argc, char **argv) {
    bar X[2]{};
    return 0;
}

When I compile it with clang++ -std=c++11 test.cc, the program produces the following output:

foo constructed.
foo constructed.
foo destroyed.

but I expected an additional "foo destroyed." between the two "foo constructed." lines. Why is only one foo destroyed? This happens with clang 3.5.1 as well as 3.6.0.

wonderingnewbie
  • 681
  • 4
  • 7
  • 3
    GCC 4.9 outputs `foo constructed. foo destroyed. foo constructed. foo destroyed.` Clang 3.5 and 3.6 reproduce your results, though. I'm guessing it's a bug in clang (using libstdc++ with clang still produces the erroneous results, so I don't think its a standard library issue). – Cornstalks Mar 11 '15 at 15:43
  • 1
    Try `endl` instead of `\n`. – Emil Laine Mar 11 '15 at 15:43
  • What version of clang? Why does nobody ever write down the @~!#ing version number?? – Lightness Races in Orbit Mar 11 '15 at 15:43
  • Ideone [does the same](http://ideone.com/gCIS2S) as GCC 4.9 – wolfPack88 Mar 11 '15 at 15:43
  • 5
    @zenith: Streams are flushed when the program ends, bub! – Lightness Races in Orbit Mar 11 '15 at 15:43
  • @wolfPack88: That _is_ GCC 4.9[.2]. – Lightness Races in Orbit Mar 11 '15 at 15:44
  • @zenith at termination the paths are closed, and when a path is closed, it gets flushed. This is just the way any sane operating system handles things. – mah Mar 11 '15 at 15:45
  • @zenith: Um the fundamentals of C++? Streams are flushed when they go out of scope, `std::cout` is an object with `static` storage duration, and such objects are destroyed after `main` returns. – Lightness Races in Orbit Mar 11 '15 at 15:45
  • @LightnessRacesinOrbit: Thought it was an older version somehow... Either way, I figured I'd provide a link – wolfPack88 Mar 11 '15 at 15:46
  • @LightnessRacesinOrbit Fair enough. – Emil Laine Mar 11 '15 at 15:51
  • 4
    In case no one has tried, replace `[2]` with `[10]` and the results are similar; 10 constructions, but still one destruction. (OS X clang 3.5). Also, test with removing `{}`. That difference one I particularly like. – WhozCraig Mar 11 '15 at 15:51
  • Clang with -lstdc++ indeed prints only one destruction. And gdb with breakpoint in destructor ~foo() lands only one time in destructor. – Erik Alapää Mar 11 '15 at 15:52
  • 1
    FWIW, http://goo.gl/fkw6dq – chris Mar 11 '15 at 15:53
  • This is interesting. – Lightness Races in Orbit Mar 11 '15 at 16:00
  • It must be some sort of bug with `{}` because it works without. The program is well-formed. – edmz Mar 11 '15 at 16:00
  • 1
    In `-O0` the destructor is called twice; in `-O1` and above it is only called once. – rwong Mar 11 '15 at 16:01
  • @rwong, the optimization level doesn't make a difference here. – wonderingnewbie Mar 11 '15 at 16:03
  • @rwong I concur with @wonderingnewbie. -O0 through -O3 makes no difference on my clang 3.5. Removal or retention of the initializer list `{}` on the array definitely alters behavior (I'm likewise using libc++ for all of this in case anyone cares). – WhozCraig Mar 11 '15 at 16:10
  • wonderingnewbie I was using the link provided by chris. The setting was x86 GCC 4.9.2. (To clarify, I'm not implying that it is a lesser issue; I'm just sharing ways to probe around the problem, such as optimization flags and settings to help diagnose the cause. IMHO it does look like a compiler optimization bug.) – rwong Mar 11 '15 at 16:46
  • 1
    @rwong in `-O0` the destructor is called twice -- but the constructor only once! In `-O1` both are called once (correct behaviour). This in Chris's link which goes to clang 3.7 – M.M Mar 11 '15 at 22:11

1 Answers1

7

Thanks for all the people who tested it! This seems to be a bug in clang. I'd appreciate if someone reports it to llvm.org. My few bugs reports there were, let say, not really helpful, so I am not looking to repeat that experience.

wonderingnewbie
  • 681
  • 4
  • 7
  • 14
    Done https://llvm.org/bugs/show_bug.cgi?id=22877 (including turning it into a memory leak that shows up with asan and valgrind) – Jonathan Wakely Mar 11 '15 at 22:33
  • 1
    I find this questions fascinating since it seems there has been some sort of `{}` initializer issue with all the major compilers with default arguments. For example see [std::map argument with empty brace-initializers for default argument segfaults in GCC](http://stackoverflow.com/q/28837142/1708801) and [(Known) compiler bug in VC12?](http://stackoverflow.com/q/21044488/1708801). – Shafik Yaghmour Mar 17 '15 at 03:46
  • @shafik my thoughts exactly. This exact issue turned up in gcc last year! http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60367 – boycy Mar 17 '15 at 21:56
  • 1
    Sad state, Jonathan reported the bug over an year ago and they still haven't fixed it. :( – wonderingnewbie Jul 08 '16 at 19:41
  • I just fixed this in Clang trunk, the fix will be in Clang 5 and should also be in Clang 4.0.1. – Richard Smith May 11 '17 at 00:33