Рассмотрим фрагмент общего кода 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 для этого типа. Ответ на https://stackoverflow.com/a/21772973/1915854 предлагает определить 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<<
может быть подводный камень)?
Это 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
, В противном случае вы позволяете программе находиться в несогласованном или небезопасном состоянии.
РЕДАКТИРОВАТЬ: Просто увидел, что вы на самом деле используете обработку исключений. Вложенная попытка / отлов, вероятно, будет оптимальной, поскольку вы можете перехватить обе ошибки там, где они могут быть обработаны.