Veja o código a seguir contendo 3 implementações de uma função que chama outra função de lançamento.
# include <stdexcept>
void f()
{
throw std::runtime_error("");
}
void g1()
{
f();
}
void g2() noexcept
{
f();
}
void g3() noexcept
{
try{ f(); } catch(...){ std::terminate(); }
}
int main()
{
return 0;
}
No meu entendimento da noexcept
especificação, g2
e g3
são estritamente equivalentes. Mas, quando compilo no Compiler Explorer com GCC, o código gerado é estritamente equivalente para g1
and g2
, mas não para g3
:
g1():
push rbp
mov rbp, rsp
call f()
nop
pop rbp
ret
g2():
push rbp
mov rbp, rsp
call f()
nop
pop rbp
ret
g3():
push rbp
mov rbp, rsp
call f()
jmp .L9
mov rdi, rax
call __cxa_begin_catch
call std::terminate()
.L9:
pop rbp
ret
Por que ?
Da forma como as exceções são implementadas no GCC, não há necessidade de emitir código extra
noexcept
ethrows
verificações. O compilador cria diversas tabelas com informações sobre todas as funções, suas variáveis de pilha e exceções que podem lançar. Quando uma exceção é lançada, essas informações são usadas para desenrolar a pilha de chamadas. É durante o desenrolamento da pilha que a biblioteca padrão notará que há umanoexcept
entrada na pilha e callstd::terminate
. Portanto, há uma diferença entreg1
and , mas nãog2
está na seção do binário gerado, mas em algum lugar em , or . Eles não são mostrados por godbolt.org..text
.eh_frame
eh_frame_hdr
.gcc_except_table
Se você executar essas funções envolvidas em
try-catch
frommain
, observará que de fato, apesar do códigog2
não ter nada extra em comparação comg1
, a execução não alcançará a cláusula catch emmain
estd::terminate
anteriores. Grosso modo, issostd::terminate
acontecerá durante a execuçãothrow
emf
.Quanto ao motivo pelo qual
g3
o código é diferente, provavelmente é porque o otimizador não conseguiu examinar toda essa lógica de tratamento de exceções envolvida e, portanto, não alterou muito o código gerado inicialmente.EDIT : Na verdade, godbolt.org pode exibir diretivas ASM relacionadas que preenchem as tabelas se você desabilitar o filtro para diretivas.
contra
Observe as linhas extras
Lendo a referência noexcept em cppreference.com cheguei à conclusão de que o código compilado não é afetado por
noexcept
É apenas uma avaliação do tempo de compilação e, portanto, não influencia a saída do compilador se usado sozinho.