So, I went to replicate the bottom case, and particularly want to do it more "fairly" by actually isolating the resulting code rather than staring at a ton of complex intermediate assembly :/. I'm thereby building a real binary--after carefully separating the compilation stages using -c and avoiding any linker optimization--instead of -S and then using objdump -d and then--because this is the whole point of talking about the happy path--I'm only providing the code of the actual functions and not any subsequent exception landing pads.

Without exceptions:

0000000000401110 <main>:
401110: 50 push %rax
401111: e8 2a 00 00 00 callq 401140 <construct>
401116: e8 25 00 00 00 callq 401140 <construct>
40111b: e8 20 00 00 00 callq 401140 <construct>
401120: e8 2b 00 00 00 callq 401150 <destruct>
401125: e8 26 00 00 00 callq 401150 <destruct>
40112a: e8 21 00 00 00 callq 401150 <destruct>
40112f: b8 08 00 00 00 mov $0x8,%eax
401134: 59 pop %rcx
401135: c3 retq

With exceptions:

0000000000401160 <main>:
401160: 50 push %rax
401161: 48 89 e7 mov %rsp,%rdi
401164: e8 37 00 00 00 callq 4011a0 <A<5u, void>::A()>
401169: e8 a2 00 00 00 callq 401210 <destruct>
40116e: e8 9d 00 00 00 callq 401210 <destruct>
401173: e8 98 00 00 00 callq 401210 <destruct>
401178: b8 08 00 00 00 mov $0x8,%eax
40117d: 59 pop %rcx
40117e: c3 retq

00000000004011a0 <A<5u, void>::A()>:
4011a0: 53 push %rbx
4011a1: e8 5a 00 00 00 callq 401200 <construct>
4011a6: e8 55 00 00 00 callq 401200 <construct>
4011ab: e8 50 00 00 00 callq 401200 <construct>
4011b0: 5b pop %rbx
4011b1: c3 retq

Now, first off, I hope people can see that this is a much more fair comparison. I am not sure why the author of this article wanted to use -S, but it seems more than a bit disingenuous to be complaining about how "tidy" the happy path is after pasting a million assembler directives at us as if those actually have a runtime performance cost instead of actually looking at the resulting execution paths through the assembly that results.

(And I want to be very clear here: this isn't because I somehow got a different result. If you carefully read through the assembly presented in the article and mentally skip all the assembler directives, you will see they got something similar, with a call to the constructor followed by three calls to destruct immediately one after the other. That said, I am not sure if the author actually understood what they were looking at, as they didn't provide the code for __ZN1AILj5EvEC2Ev, which is clearly part of the happy path.)

But... yeah: it added the cost of a single extra call indirection for some reason. I'm not entirely sure why, but this frankly doesn't feel like a ridiculous cost. I will also note that it actually generates identical code to the case without exceptions if you merely add a single "inline" keyword to the code on the constructor for A (note: I mean the one on line 9 that is ostensibly empty, not the one on line 16). That said, you also probably wouldn't actually WRITE such a no-op constructor in the first place, and it turns out deleting it also fixes the resulting code to be exactly the same with and without exceptions, so I'm not sure of the applicability here... this just feels like such a corner case :/.

(The actual reasonable complaint to make about "zero cost" exceptions--which this article didn't make, because it seems that it didn't occur to the author to do an actual timing analysis, which is where I was hoping this article would go--is that the exception tables tend to get mixed in with the rest of the normal functions, which massively pads out the code space, which will lead to many more TLB misses and page fetches while executing the happy path. There should be a way to move these landing pads all to the end of the executable to be loaded into memory only if absolutely necessary; but, if that's already possible, I personally don't know how to do that :(.)