Оператор << (вывод потока) для nullptr
Рассмотрим фрагмент общего кода C++, который выводит в поток значения своих аргументов, если они не равны:
#define LOG_IF_NE(a, b) if(a != b) { \
std::cerr << "Failed because (" << ##a << "=" << (a) << \
") != (" << ##b << "=" << (b) << ")"; \
}
Это всего лишь пример, настоящий код генерирует исключение после записи сообщения в поток строк. Это прекрасно работает для 2 целых, 2 указателей и т. Д. Для какого потока operator <<
определено.
int g_b;
int f(int a)
{
LOG_IF_NE(a, g_b);
// implementation follows
}
Проблема возникает, когда один из аргументов LOG_IF_NE
является nullptr
: Компилятор MSVC++2013 дает error C2593: 'operator <<' is ambiguous
,
int *pA;
int g()
{
LOG_IF_NE(pA, nullptr);
}
Проблема происходит потому, что nullptr
имеет специальный тип и operator <<
не определен в STL для этого типа. Ответ на /questions/20006021/nevozmozhno-napechatat-znachenie-nullptr-na-ekrane/20006029#20006029 предлагает определить operator <<
за std::nullptr_t
//cerr is of type std::ostream, and nullptr is of type std::nullptr_t
std::ostream& operator << (std::ostream& os, std::nullptr_t)
{
return os << "nullptr"; //whatever you want nullptr to show up as in the console
}
Это правильный способ решить проблему? Разве это не ошибка в C++11/STL, которая operator<<
не определен для nullptr_t
? Ожидается ли исправление в C++14/17? Или это было сделано нарочно (поэтому частное определение operator<<
может быть подводный камень)?
2 ответа
Это LWG № 2221, которая предлагает:
Очевидное решение для библиотеки - добавить
nullptr_t
перегрузка, которая была бы определена что-то вродеtemplate<class C, class T> basic_ostream<C, T>& operator<<(basic_ostream<C, T>& os, nullptr_t) { return os << (void*) nullptr; }
Мы могли бы также рассмотреть решение этой проблемы на уровне ядра: добавить специальное языковое правило, которое учитывает все случаи, когда вы пишете
f(nullptr)
а такжеf
перегружен на несколько типов указателей. (Возможно, тайлбрейкер говорил, чтоvoid*
является предпочтительным в таких случаях.)
Это не в C++14, я не знаю, превратится ли это в C++17 или нет. Это очень простая проблема, которую можно решить самостоятельно, поэтому она не имеет особого значения в плане изменений стандартов. Как вы сами сказали в вопросе - это всего лишь трехстрочная функция.
Я думаю, что это могло быть преднамеренным по той же причине, что nullptr
это его собственная ценность. Безмолвное принятие его в этой ситуации может рассматриваться как потенциальное нарушение предварительных условий и инвариантов.
nullptr
это значение для инициализации указателя, чтобы определить, что он не был инициализирован. Таким образом, по этой логике фактическое ее использование в любом случае должно быть явным и документированным, чтобы предотвратить злоупотребление и потенциальные дыры в безопасности. Простая перегрузка оператора для его распечатки в любом случае не обеспечит этого.
В среде отладки (где ваш макрос будет вызывать home) гораздо больше смысла использовать утверждения с условной компиляцией, если вы проверяете инварианты и программную логику с четко определенными выходными данными для всего остального.
Это в основном сводится к проектной точке: обрабатывать ошибки, из которых их лучше всего восстанавливать. Ваш макрос проверяет неравенство, но если он обнаруживает nullptr
для этого макроса не имеет смысла обрабатывать эту ошибку, поэтому откат к обработке исключений имеет больше смысла, поэтому проблема может быть выброшена куда-то, что может обработать и восстановить данные. nullptr
, В противном случае вы позволяете программе находиться в несогласованном или небезопасном состоянии.
РЕДАКТИРОВАТЬ: Только что увидел, что вы на самом деле используете обработку исключений. Вложенный try / catch, вероятно, будет оптимальным, потому что вы можете перехватить обе ошибки там, где они могут быть обработаны.