Considere o seguinte programa:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
GCC e Clang com chamada libstdc ++ std::terminate
e aborte o programa com a mensagem
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Clang com libc ++ segfaults na construção da exceção.
Veja Godbolt .
Os compiladores estão se comportando em conformidade com o padrão? A seção relevante do padrão [diagnostics.range.error] (C ++ 17 N4659) diz que std::range_error
possui uma const char*
sobrecarga de construtor que deve ser preferida à const std::string&
sobrecarga. A seção também não declara pré-condições no construtor e apenas a pós-condição
Pós-condições :
strcmp(what(), what_arg) == 0
.
Essa pós-condição sempre tem um comportamento indefinido se what_arg
for um ponteiro nulo; isso significa que meu programa também tem um comportamento indefinido e que os dois compiladores agem em conformidade? Caso contrário, como se deve ler essas pós-condições impossíveis no padrão?
Pensando bem, acho que deve significar um comportamento indefinido para o meu programa, porque, se isso não significasse, ponteiros (válidos) que não apontam para seqüências terminadas em nulo também seriam permitidos, o que claramente não faz sentido.
Então, supondo que isso seja verdade, eu gostaria de focar mais a questão em como o padrão implica esse comportamento indefinido. Resulta da impossibilidade da pós-condição que a chamada também tenha um comportamento indefinido ou a pré-condição foi simplesmente esquecida?
Inspirado por esta pergunta .
what()
quandonullptr
é passado provavelmente causaria problemas.nullptr
for aprovada, eu acho quewhat()
teria que desreferenciar isso em algum momento para obter o valor. Isso seria desreferenciar anullptr
, o que é problemático na melhor das hipóteses e com certeza travar é o pior.strcmp
é usada para descrever o valor dewhat_arg
. É o que a seção relevante do padrão C diz de qualquer maneira, a que se refere a especificação de<cstring>
. Claro que o texto poderia ser mais claro.Respostas:
Do documento :
Isso mostra por que você obtém o segfault, a API realmente o trata como uma string real. Em geral no cpp, se algo for opcional, haverá um construtor / função sobrecarregado que não aceita o que não é necessário. Portanto, passar
nullptr
para uma função que não documenta algo opcional é um comportamento indefinido. Normalmente, as APIs não usam ponteiros, com exceção das seqüências C. Portanto, no IMHO, é seguro assumir que passar nullptr para uma função que esperaconst char *
a será um comportamento indefinido. APIs mais recentes podem preferirstd::string_view
para esses casos.Atualizar:
Geralmente, é justo supor que uma API C ++ use um ponteiro para aceitar NULL. No entanto, as strings C são um caso especial. Até que
std::string_view
não havia melhor maneira de passar com eficiência. Em geral, para uma API aceitarconst char *
, deve-se supor que ela deve ser uma cadeia C válida. ou seja, um ponteiro para uma sequência dechar
s que termina com um '\ 0'.range_error
pode validar que o ponteiro não é,nullptr
mas não pode validar se terminar com um '\ 0'. Portanto, é melhor não fazer nenhuma validação.Não sei o texto exato do padrão, mas essa pré-condição provavelmente é assumida automaticamente.
fonte
Isso remonta à pergunta básica. Não há problema em criar um std :: string a partir do nullptr? e o que deveria fazer?
www.cplusplus.com diz
Então quando
é chamado a implementação tenta fazer algo como
que é indefinido. O que eu consideraria um bug (sem ter lido a redação real no padrão). Em vez disso, os desenvolvedores da biblioteca poderiam ter criado algo como
Mas o apanhador precisa procurar o nullptr
what();
ou ele simplesmente trava ali. Portanto,std::range_error
deve-se atribuir uma string vazia ou "(nullptr)", como alguns outros idiomas.fonte
std::string
e ostd::string
construtor não deve ser escolhido por resolução de sobrecarga.const char *
sobrecarga é selecionada por resolução de sobrecarga