Рассмотрим следующую программу:
#include<stdexcept>
#include<iostream>
int main() {
try {
throw std::range_error(nullptr);
} catch(const std::range_error&) {
std::cout << "Caught!\n";
}
}
GCC и Clang с помощью libstdc ++ вызывают std::terminate
и отменяют программу с сообщением
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct null not valid
Clang с libc ++ segfaults при построении исключения.
Вижу Годболт .
Ведут ли компиляторы стандартное соответствие? В соответствующем разделе стандарта [ Diagnics.range.error ] (C ++ 17 N4659) сказано, что std::range_error
имеется const char*
перегрузка конструктора, которая должна быть предпочтительнее const std::string&
перегрузки. В разделе также не указываются никакие предварительные условия для конструктора, а только указывается постусловие.
Постусловия :
strcmp(what(), what_arg) == 0
.
Это постусловие всегда имеет неопределенное поведение, если what_arg
является нулевым указателем, значит ли это, что моя программа также имеет неопределенное поведение и что оба компилятора действуют согласованно? Если нет, то как следует читать такие невозможные постусловия в стандарте?
Во-вторых, я думаю, что это должно означать неопределенное поведение для моей программы, потому что, если это не так, то (допустимые) указатели, не указывающие на строки с нулевым символом в конце, также будут разрешены, что явно не имеет смысла.
Итак, предполагая, что это правда, я хотел бы сосредоточить вопрос больше на том, как стандарт подразумевает это неопределенное поведение. Из невозможности постусловия следует, что вызов также имеет неопределенное поведение, или предусловие было просто забыто?
Вдохновлен этим вопросом .
источник
what()
когдаnullptr
прошло, вероятно, вызовет проблемы.nullptr
будет принято, я думаю, чтоwhat()
в какой-то момент придется разыменовать его, чтобы получить значение. Это было бы разыменованием anullptr
, которое в лучшем случае проблематично, а наверняка к краху - хуже всего.strcmp
используется для описания значенияwhat_arg
. Это то, что в любом случае говорится в соответствующем разделе стандарта C , на который ссылается спецификация<cstring>
. Конечно, формулировка может быть более понятной.Ответы:
Из документа :
Это показывает, почему вы получаете segfault, API действительно обрабатывает его как настоящую строку. В общем случае в cpp, если что-то было необязательным, будет перегруженный конструктор / функция, которая не берет то, что ей не нужно. Таким образом, передача
nullptr
функции, которая не документирует что-то необязательное, будет неопределенным поведением. Обычно API не используют указатели, за исключением строк C. Так что ИМХО можно предположить, что передача nullptr для функции, которая ожидаетconst char *
, будет неопределенным поведением. Новые API могут предпочестьstd::string_view
для этих случаев.Обновить:
Обычно справедливо предположить, что C ++ API принимает указатель для принятия NULL. Однако строки C являются особым случаем. Пока
std::string_view
не было лучшего способа их эффективно передать. В общем случае для принятия APIconst char *
предполагается, что это должна быть допустимая строка C. то есть указатель на последовательностьchar
s, оканчивающуюся на '\ 0'.range_error
может подтвердить, что указатель не является,nullptr
но он не может проверить, если он заканчивается на «\ 0». Так что лучше не делать никаких проверок.Я не знаю точную формулировку в стандарте, но это предварительное условие, вероятно, предполагается автоматически.
источник
Это возвращает нас к основному вопросу: можно ли создавать std :: string из nullptr? и что он должен это сделать?
www.cplusplus.com говорит
Так когда
называется реализация пытается сделать что-то вроде
который не определен. Что я бы посчитал ошибкой (не прочитав фактическую формулировку в стандарте). Вместо этого разработчики свободы могли бы сделать что-то вроде
Но тогда ловец должен проверить на наличие nullptr,
what();
иначе он просто там вылетит. Поэтомуstd::range_error
следует присваивать либо пустую строку, либо "(nullptr)", как это делают некоторые другие языки.источник
std::string
иstd::string
конструктор не должен выбираться с помощью разрешения перегрузки.const char *
Перегрузки выбирается разрешения перегрузки