Nano Hash - криптовалюты, майнинг, программирование

оператор ‹‹ (потоковый вывод) для 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 для этого типа. Ответ на 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<< могло иметь ловушку)?


Ответы:


1

Это LWG #2221, в котором предлагается:

Очевидным библиотечным решением является добавление nullptr_toverload, которое будет определено примерно так:

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*.)

Его нет в С++ 14, я не знаю, попадет ли он в С++ 17. Эту проблему очень легко решить самостоятельно, поэтому она не является особо приоритетной в плане изменения стандартов. Как вы сами сказали в вопросе, это всего лишь трехстрочная функция.

01.07.2015
  • Разве второй аргумент не должен быть константой? const nullptr_t 01.07.2015
  • @SergeRogatch: передается как значение. 01.07.2015

  • 2

    Я думаю, что это могло быть сделано намеренно по той же причине, по которой nullptr является собственным значением. Молчаливо принять его в этой ситуации можно рассматривать как потенциальное нарушение предварительных условий и инвариантов.

    nullptr — это значение для инициализации указателя, чтобы определить, что он не был инициализирован. Таким образом, согласно этой логике, его фактическое использование в любом случае должно быть явным и задокументированным, чтобы предотвратить неправильное использование и потенциальные дыры в безопасности. Простая перегрузка оператора для его вывода в любом случае не обеспечит этого.

    В среде отладки (где ваш макрос будет называться домом) гораздо больше смысла использовать утверждения с условной компиляцией, если вы проверяете инварианты и программную логику с четко определенными выходными данными для всего остального.

    В основном это сводится к точке дизайна: обрабатывать ошибки там, где их лучше всего исправить. Ваш макрос проверяет неравенство, но если он обнаружит nullptr, для этого макроса не имеет смысла обрабатывать эту ошибку, поэтому имеет смысл вернуться к обработке исключений, чтобы проблема могла быть брошена туда, где можно обработать и восстановиться после nullptr . В противном случае вы позволяете программе находиться в несогласованном или небезопасном состоянии.

    EDIT: Только что увидел, что вы на самом деле используете обработку исключений. Вложенная попытка/поймать, вероятно, была бы оптимальной, потому что вы могли бы поймать обе ошибки там, где их можно обработать.

    01.07.2015
  • Если я просто использую 'assert(pA != nullptr)' и это не удается, он не будет печатать значения переменных. Для получения значений переменных в журналах и методе std::exception what() используется stringstream (вместо cerr, последний был просто для предоставления минимального примера кода) и класс, производный от std::exception, инициализируется сообщением, полученным из нескольких токенов, хранящихся в stringstream, с использованием оператора‹‹ . Вот почему важно, чтобы код компилировался, когда один из аргументов имеет значение nullptr. 01.07.2015
  • Это имеет больше смысла, поскольку функциональность используется в коде ошибки. IMHO просто кажется, что это открывает дверь для ошибок и потенциальных векторов атаки. 02.07.2015
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..