Na biblioteca padrão, o tipo de parâmetro do construtor da classe de exceção, como std::runtime_error
, segue este padrão:
runtime_error( const std::string& what_arg ); // (1)
runtime_error( const char* what_arg ); // (2)
runtime_error( const runtime_error& other ); // (3)
O que me confundiu é que (1) foi usado const std::string&
em vez de std::string
, o que obviamente resulta em uma operação de cópia adicional dentro do construtor.
Se o tipo de parâmetro fosse std::string
, não apenas evitaria uma operação de cópia extra, mas também seria noexcept
; o que é uma conclusão que percebi recentemente:
Se uma função marcada como noexcept
tiver um parâmetro de um tipo que pode gerar uma exceção durante sua construção, mesmo que seja garantido que a exceção será gerada, ela não fará com que o programa seja encerrado devido a uma exceção.
Isto é confirmado pelo programa de teste .
Então, por que a lista de parâmetros do construtor de exceção da biblioteca padrão não usa std::string
? Ou meu entendimento e conclusão, assim como o programa de teste, estão incorretos, mesmo que seja um comportamento indefinido?
Acho que você está assumindo que isso
std::runtime_error
é implementado com umastd::string
variável de membro para armazenarwhat()
. Este não é necessariamente o caso, por exemplo , libstdc++ tem um__cow_string
tipo de membro especial , libc++ usa umstd::__libcpp_refstring
tipo de membro especial , MSVC usa um__std_exception_data
tipo.Ter uma sobrecarga tomando
std::string
por valor introduziria, na verdade, uma cópia extra para implementações que não têm umstd::string
membro (uma cópia para o temporáriostd::string
e uma segunda para o formato interno). Uma sobrecarga de referência de valor r pelo menos não teria nenhuma desvantagem sobre aconst
referência, mas acho que ninguém sentiu que isso vale o esforço de padronização desde que os valores r foram adicionados em c++11 (presumivelmente porque nenhuma implementação realmente usa umstd::string
membro).Exceções geralmente não são implementadas usando
std::string
membros para evitar os problemas que você mencionou. As alocações podem falhar ao criar exceções (especialmente se a exceção estiver sendo gerada devido ao esgotamento da memória), então geralmente há algum alocador especializado usado que não falhará mesmo se não houver espaço de heap geral disponível.