3

Code using noexcept .

//hello.cpp
class A{
public:
    A(){}
    ~A(){}
    
};
void fun() noexcept{ //c++11 style
    A a[10];
}
int main()
{
    fun();
}

Code using throw() .

//hello1.cpp
class A{
public:
    A(){}
    ~A(){}
    
};
void fun() throw(){//c++98 style
    A a[10];
}
int main()
{
    fun();
}

As per various online links and scott meyer's book "If, at runtime, an exception leaves fun, fun’s exception specification is violated. With the C++98 exception specification, the call stack is unwound to f’s caller, and, after some actions not relevant here, program execution is terminated. With the C++11 exception specification, runtime behavior is slightly different: the stack is only possibly unwound before program execution is terminated." He said code using noexcept is more optimized than code using throw() .

But when I have generated machine code for above program , i found code generated for both cases is exactly same .

$ g++ --std=c++11 hello1.cpp -O0 -S -o throw1.s

$ g++ --std=c++11 hello.cpp -O0 -S -o throw.s

diff is below .

$ diff throw.s throw1.s 
1c1
<   .file   "hello.cpp"
---
>   .file   "hello1.cpp"

Machine code generated is as below for function "fun" for both cases .

.LFB1202:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        pushq   %r12
        pushq   %rbx
        subq    $16, %rsp
        .cfi_offset 12, -24
        .cfi_offset 3, -32
        leaq    -32(%rbp), %rax
        movl    $9, %ebx
        movq    %rax, %r12
        jmp     .L5
.L6:
        movq    %r12, %rdi
        call    _ZN1AC1Ev
        addq    $1, %r12
        subq    $1, %rbx
.L5:
        cmpq    $-1, %rbx
        jne     .L6
        leaq    -32(%rbp), %rbx
        addq    $10, %rbx
.L8:
        leaq    -32(%rbp), %rax
        cmpq    %rax, %rbx
        je      .L4
        subq    $1, %rbx
        movq    %rbx, %rdi
        call    _ZN1AD1Ev
        jmp     .L8
.L4:
        addq    $16, %rsp
        popq    %rbx
        popq    %r12
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1202:
        .size   _Z3funv, .-_Z3funv
        .globl  main
        .type   main, @function

What is advantage of using noexcept when noexcept and throw are generating the same code ?

Community
  • 1
  • 1
SACHIN GOYAL
  • 955
  • 6
  • 19
  • 4
    Er... you're not throwing anything. – erip Jan 19 '16 at 13:04
  • 3
    You may need to compile with optimizations on to see a difference. – NathanOliver Jan 19 '16 at 13:06
  • @NathanOliver: which optimization would you prefer ? – SACHIN GOYAL Jan 19 '16 at 13:07
  • @NathanOliver : I just tried with O3 ,result is still same .$ g++ --std=c++11 hello1.cpp -O3 -S -o throw1.s $ diff throw.s throw1.s 1c1 < .file "hello.cpp" --- > .file "hello1.cpp" – SACHIN GOYAL Jan 19 '16 at 13:09
  • @erip : If I will throw something that would be another story as noexcept but I have seen people suggesting "noexcept" even when your code is not throwing anything . – SACHIN GOYAL Jan 19 '16 at 13:11
  • Well I get [this](http://goo.gl/XfFXXE) with -O3. I get the same code but that is not surprising as you are creating a stacked based array. – NathanOliver Jan 19 '16 at 13:11
  • It can quite likely see that neither function throws anything, so why should the code be different? – DavidW Jan 19 '16 at 13:12
  • 2
    Try actually throwing an exception within `fun()` and see if that changes anything. The compiler can certainly detect no exception is being thrown in your code, which means there is neither an actual need (C++98) nor potential need (C++11) to unwind the stack. – Peter Jan 19 '16 at 13:12
  • From cppreference: _noexcept will not call std::unexpected and **may or may not unwind the stack**_ – erip Jan 19 '16 at 13:13
  • Certainly , code will differ when i will throw something as erip pointed "noexcept will not call std::unexpected and may or may not unwind the stack" .But do you mean there is no difference between the two until and unless I am throwing anything ? why would I want to throw from a function which is using noexcept (hence grantee that it will not throw anything ? ) . – SACHIN GOYAL Jan 19 '16 at 13:17
  • @SACHINGOYAL It's not so much a question of why would you *want* to throw, but more why you *would* throw. For example, you missed something. The only question is, what do you expect the compiler to do in such a case? In C++11, the rules have changed to allow the compiler to implement more optimizations, or at the very least *not* implement certain guarantees. – Yam Marcovic Jan 19 '16 at 13:19
  • @NathanOliver : how does stack based array makes difference ? could you please elaborate? – SACHIN GOYAL Jan 19 '16 at 13:24
  • 1
    It generates different codes on my OSX 10.11 Apple LLVM version 7.0.2 (clang-700.1.81), at least a call to terminate handler and different exception tables – Jean-Baptiste Yunès Jan 19 '16 at 13:44
  • 3
    When the compiler can see your entire program, and it is simple, they can *know* if your code actually throws. Nothing throws, so the compiler is free to eliminate throw handling: heck, it can optimize your program to `int main(){}`. You must find a way (say, possibly throw depending on user input -- you go and call `new` unguarded with a number of bytes determined by the user) to prevent such optimizations, while still allowing some to occur. – Yakk - Adam Nevraumont Jan 19 '16 at 19:24

1 Answers1

1

They are generating the same code because you are not throwing anything. Your test program is so simple that the compiler can trivially analyze it, determine that it is not throwing an exception, and in fact not doing anything at all! With optimizations enabled (-O1 and higher), the object code:

fun():
        rep ret
main:
        xor     eax, eax
        ret

shows that your test code is being optimized simply to the most trivial valid C++ application:

int main()
{
    return 0;
}

If you want to really test the difference in object code generation for the two types of exception specifiers, you need to use a real (i.e., non-trivial) test program. Something that actually throws an exception, and where that throw cannot be analyzed out by a bit of compile-time analysis:

void fun(int args) throw()  // C++98 style
{
   if (args == 0)
   {
      throw "Not enough arguments!";
   }
   else
   {
      // do something
   }
}

int main(int argc, char** argv)
{
   fun(argc);
   return 0;
}

In this code, an exception is conditionally thrown depending on the value of an input parameter (argc) passed to the main function. It is impossible for the compiler to know, at compile-time, what the value of this argument will be, so it cannot optimize out either this conditional check or the throwing of the exception. That forces it to generate exception-throwing and stack-unwinding code.

Now we can compare the resulting object code. Using GCC 5.3, with -O3 and -std=c++11, I get the following:

C++98 style (throw())

.LC0:
        .string "Not enough arguments!"
fun(int):
        test    edi, edi
        je      .L9
        rep ret
.L9:
        push    rax
        mov     edi, 8
        call    __cxa_allocate_exception
        xor     edx, edx
        mov     QWORD PTR [rax], OFFSET FLAT:.LC0
        mov     esi, OFFSET FLAT:typeinfo for char const*
        mov     rdi, rax
        call    __cxa_throw
        add     rdx, 1
        mov     rdi, rax
        je      .L4
        call    _Unwind_Resume
.L4:
        call    __cxa_call_unexpected
main:
        sub     rsp, 8
        call    fun(int)
        xor     eax, eax
        add     rsp, 8
        ret

C++11 style (noexcept)

.LC0:
        .string "Not enough arguments!"
fun(int) [clone .part.0]:
        push    rax
        mov     edi, 8
        call    __cxa_allocate_exception
        xor     edx, edx
        mov     QWORD PTR [rax], OFFSET FLAT:.LC0
        mov     esi, OFFSET FLAT:typeinfo for char const*
        mov     rdi, rax
        call    __cxa_throw
fun(int):
        test    edi, edi
        je      .L8
        rep ret
.L8:
        push    rax
        call    fun(int) [clone .part.0]
main:
        test    edi, edi
        je      .L12
        xor     eax, eax
        ret
.L12:
        push    rax
        call    fun(int) [clone .part.0]

Note that they are clearly different. Just as Meyers et al. have claimed, the C++98 style throw() specification, which indicates that a function does not throw, causes a standards-compliant compiler to emit code to unwind the stack and call std::unexpected when an exception is thrown from inside of that function. That is exactly what happens here. Because fun is marked throw() but in fact does throw, the object code shows the compiler emitting a call to __cxa_call_unexpected.

Clang is also standards-compliant here and does the same thing. I won't reproduce the object code, because it's longer and harder to follow (you can see it on Matt Godbolt's excellent site), but putting the C++98 style exception specification on the function causes the compiler to explicitly call std::terminate if the function throws in violation of its specification, whereas the C++11 style exception specification does not result in a call to std::terminate.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574