Подтвердить что ты не робот

Почему функция преобразования void *() оператора добавлена ​​в классы потоков С++?

В потоковых классах С++ есть функция преобразования operator void*() const. так что все объекты потока могут быть неявно преобразованы в void*. Во время взаимодействия с программистами на SO они предлагают мне не использовать void*, если у вас нет веских оснований для его использования. void* - это метод устранения безопасности типов и проверки ошибок. Таким образом, из-за существования этой функции преобразования следующая программа совершенно верна. Это недостаток в стандартной библиотеке С++.

#include <iostream>
int main()
{
       delete std::cout;
       delete std::cin;
}

Смотрите демо-версию здесь.

Вышеприведенная программа действительна в С++ 03, но не скомпилирована в компиляторах С++ 11 и более поздних версий, поскольку эта функция преобразования удалена. Но вопрос в том, почему он был частью стандартной библиотеки С++, если он опасен? Какова была цель разрешить преобразование объектов потока в void*? Какая польза от этого?

4b9b3361

Ответ 1

Особенностью std::stringstream является то, что предполагается, что если поток используется как bool, он преобразуется в true, если поток по-прежнему действителен и false, если он отсутствует. Например, это позволяет использовать простой синтаксис, если вы реализуете свою собственную версию лексического перевода.

(Для справки boost содержит функцию шаблона под названием lexical_cast, которая делает что-то похожее на следующую простую функцию шаблона.)

template <typename T, typename U>
T lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        throw bad_lexical_cast();
    }
 }

Если, например, вы работаете над проектом, в котором исключения запрещены, вы можете вместо этого перевернуть следующую версию.

template <typename T, typename U>
boost::optional<T> lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        return boost::none;
    }
 }

(Существуют различные способы улучшения вышеизложенного, этот код просто для примера).

operator void * используется в приведенном выше виде:

  • ss << u возвращает ссылку на ss.
  • ss неявно преобразован в void *, который является nullptr, если операция потока завершилась неудачно, и не имеет значения null, если поток по-прежнему хорош.
  • Оператор && прерывается быстро, в зависимости от значения истины этого указателя.
  • Вторая часть ss >> t запускается и возвращается и также преобразуется в void *.
  • Если обе операции преуспели, мы можем вернуть потоковый результат t. Если либо не удалось, мы сообщим об ошибке.

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

Недостаток фактического введения неявного преобразования bool заключается в том, что тогда std::stringstream становится неявным образом конвертируемым в int и многими другими типами, так как bool неявно конвертируется в int. Это в конечном итоге вызывает синтаксические кошмары в других местах.

Например, если std::stringstream имеет неявное преобразование operator bool, предположим, что у вас есть этот простой код:

std::stringstream ss;
int x = 5;
ss << x;

Теперь при разрешении перегрузки у вас есть две потенциальные перегрузки, чтобы рассмотреть (!), нормальный operator<<(std::stringstream &, int), и тот, в котором ss преобразуется в bool, затем продвигается до int, а оператор битового сдвига может применяться operator<<(int, int), все из-за неявного преобразования в bool...

Обходным путем является использование неявного преобразования в void * вместо этого, которое может использоваться контекстно как bool, но на самом деле неявно конвертируется в bool или int.

В С++ 11 нам больше не нужен этот обходной путь, и нет причин, чтобы кто-то явно использовал преобразование void *, поэтому он просто удалился.

(Я ищу ссылку, но я считаю, что значение этой функции было указано только тогда, когда она должна быть nullptr против того, когда она не должна быть nullptr, и что она была определена какой ненулевой показатель указателя он может дать.Таким образом, любой код, который полагался на версию void * и не может быть тривиально реорганизован для использования версии bool, в любом случае был бы некорректным.)

Обходной путь void * все еще не без проблем. В boost была разработана более сложная идиома "безопасный bool" для кода pre-С++ 11, который на основе iiuc основан на чем-то вроде T*, где t является либо "типом, используемым один раз", как структурой, которая определенные в классе, реализующем идиому, или используя функцию-указатель-член для этого класса и используя возвращаемые значения, которые являются либо частной частной функцией-членом этого класса, либо nullptr. Идиома "safe bool" используется, например, во всех интеллектуальных указателях boost.

Несмотря на то, что весь этот беспорядок был очищен на С++ 11, введя explicit operator bool.

Дополнительная информация здесь: Идиома safe-bool устарела в С++ 11?

Ответ 2

Это недостаток в стандартной библиотеке С++.

Этот был недостатком в старых версиях (1998 и 2003) стандартной библиотеки С++. Этот недостаток больше не существует в 2011 году и более поздних версиях стандарта. Новая функция, возможность отмечать операции преобразования как explicit, была добавлена ​​на язык, чтобы сделать оператор преобразования безопасным.

Разработчики исходной версии стандарта С++ явно решили использовать преобразование в void* вместо преобразования в bool, потому что неявное преобразование в bool было довольно небезопасным несколькими способами. Для сравнения, в то время как operator void*() был довольно явно клочковым, он работал, по крайней мере, до тех пор, пока вы не указали этот указатель на что-то еще или не попытались его удалить. (Альтернативный вариант operator! был, возможно, более безопасным, чем любой из этих операторов преобразования, но это потребовало бы неинтуитивного и абсурдного while (!!stream) {...}.)

Концепция "безопасного bool" идиомы была разработана после того, как была выпущена оригинальная версия стандарта 1998/1999. Был ли он разработан до 2003 года, немного неактуальен; версия стандарта 2003 года была предназначена для исправления ошибок для этого оригинального стандарта. Этот компилятор operator void*() let delete std::cin не был признан ошибкой так же, как проблема "не делай этого тогда".

Развитие "безопасного bool" идиомы показало, что существуют альтернативы, которые сделали operator bool() безопасным, но если вы посмотрите на любую из реализаций, они все массивно запутаны и массово клочены. Решение С++ 11 было удивительно простым: позволить операторам преобразования быть квалифицированными с ключевым словом explicit. Решение С++ 11 удалило оператор преобразования void* и добавило оператор преобразования explicit bool. Это сделало идиому "безопасного bool" устаревшей, по крайней мере, до тех пор, пока вы используете компилятор, совместимый с С++ 11 (или более поздним).