Considérez le programme suivant:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
GCC et Clang avec libstdc ++ appellent std::terminate
et abandonnent le programme avec le message
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Clang avec libc ++ segfaults sur la construction de l'exception.
Voir godbolt .
Les compilateurs se comportent-ils conformément aux normes? La section pertinente de la norme [diagnostics.range.error] (C ++ 17 N4659) dit qu'il y std::range_error
a une const char*
surcharge de constructeur qui devrait être préférée à la const std::string&
surcharge. La section n'énonce pas non plus de conditions préalables sur le constructeur et indique uniquement la postcondition
Postconditions :
strcmp(what(), what_arg) == 0
.
Cette postcondition a toujours un comportement indéfini s'il what_arg
s'agit d'un pointeur nul, cela signifie-t-il que mon programme a également un comportement indéfini et que les deux compilateurs agissent conformément? Sinon, comment lire ces conditions postérieures impossibles dans la norme?
À la réflexion, je pense que cela doit signifier un comportement indéfini pour mon programme, car si ce n'était pas le cas, des pointeurs (valides) ne pointant pas vers des chaînes terminées par null seraient également autorisés, ce qui n'a clairement aucun sens.
Donc, en supposant que cela soit vrai, je voudrais concentrer davantage la question sur la façon dont la norme implique ce comportement indéfini. Doit-il résulter de l'impossibilité de la postcondition que l'appel a également un comportement indéfini ou la condition préalable a-t-elle simplement été oubliée?
Inspiré par cette question .
la source
what()
quandnullptr
est passé causerait probablement des problèmes.nullptr
est passé, je pense qu'ilwhat()
faudrait le déréférencer à un moment donné pour obtenir la valeur. Ce serait déréférencer unnullptr
, ce qui est au mieux problématique et certain de planter est pire.strcmp
est utilisé pour décrire la valeur dewhat_arg
. C'est ce que dit de toute façon la section pertinente de la norme C , à laquelle fait référence la spécification de<cstring>
. Bien sûr, le libellé pourrait être plus clair.Réponses:
Du doc :
Cela montre pourquoi vous obtenez un défaut de segmentation, l'API le traite vraiment comme une vraie chaîne. En général, dans cpp, si quelque chose était facultatif, il y aura un constructeur / fonction surchargé qui ne prendra pas ce dont il n'a pas besoin. Donc, passer
nullptr
pour une fonction qui ne documente pas quelque chose comme facultatif va être un comportement non défini. Habituellement, les API ne prennent pas de pointeurs à l'exception des chaînes C. Donc, à mon humble avis, il est sûr de supposer que passer nullptr pour une fonction qui attend aconst char *
, va être un comportement non défini. Les API plus récentes peuvent préférerstd::string_view
pour ces cas.Mise à jour:
Habituellement, il est juste de supposer qu'une API C ++ prend un pointeur pour accepter NULL. Cependant, les chaînes C sont un cas particulier. Jusqu'à ce
std::string_view
qu'il n'y ait pas de meilleur moyen de les passer efficacement. En général, pour une API acceptantconst char *
, l'hypothèse devrait être qu'il doit s'agir d'une chaîne C valide. c'est-à-dire un pointeur vers une séquence dechar
s qui se termine par un '\ 0'.range_error
pourrait valider que le pointeur ne l'est pasnullptr
mais il ne peut pas valider s'il se termine par un '\ 0'. Il vaut donc mieux ne pas faire de validation.Je ne connais pas le libellé exact de la norme, mais cette condition préalable est probablement supposée automatiquement.
la source
Cela revient à la question de base est-il OK de créer une chaîne std :: à partir de nullptr? et que doit-il faire?
www.cplusplus.com dit
Donc quand
est appelé la mise en œuvre essaie de faire quelque chose comme
qui n'est pas défini. Ce que je considérerais comme un bug (sans avoir lu le libellé réel de la norme). Au lieu de cela, les développeurs Libery auraient pu faire quelque chose comme
Mais alors le receveur doit vérifier nullptr
what();
ou il va juste planter là. Donc, lestd::range_error
devrait attribuer une chaîne vide ou "(nullptr)" comme le font d'autres langues.la source
std::string
et lestd::string
constructeur ne doit pas être choisi par résolution de surcharge.const char *
surcharge est sélectionnée par résolution de surcharge