Форум » C/C++ » Что будет выведено на консоль предложением std::cout << std::cout; » Ответить

Что будет выведено на консоль предложением std::cout << std::cout;

Сыроежка: Этот вопрос я встретил на одном форуме, на котором там же и ответил. Но я решил продублировать здесь этот вопрос и дать более полный ответ на него, так как это познавательно. Итак, что будет выведено на консоль в результате выполнения предложения [pre2]std::cout << std::cout;[/pre2] На первый взгляд такая запись вообще выглядет бессмысленной, и кто-то даже может предположить, что возникнет ошибка компиляции. Каково же будет его удивление, когда он обнаружит, что код компилируется! Мы настолько привыкли использовать перегруженный оператор << для вывода на консоль фундаментальных типов или определеннных пользователем типов данных, что предполагаем, что в правой части этого выражения могут присутствовать только такие объекты указанных типов. Например, [pre2]int x = 5; char c = 'A"; char a[] = "An array."; std::cttring s( "A string." ); std::cout << x << ", " << c << ", " << a << ", " << s << std::endl;[/pre2] И даже сами часто определяем свои оператор-функции перегрузки оператора << для собственных типов. Например, [pre2]#include <iostream> struct Point { Point() : x( 0 ), y( 0 ) {} Point( int x, int y ) : x( x ), y( y ) {} int x; int y; }; std::ostream & operator <<( std::ostream &os, const Point &rhs ) { return ( os << "< " << rhs.x << ", " << rhs.y << " >" ); } int main() { Point p( 10, 20 ); std::cout << p << std::endl; }[/pre2] Но какой смысл имеет ниже приведенный оператор?! std::ostream & operator <<( std::ostream &os, const std::ostream &rhs ); Очевидно, что такого объявления в стандартной библиотеки С++ нет. Тогда что происходит, когда мы пишем std::cout << std:;cout, почему компилируется код ? Когда компилятор встречает такое выражение, то он начинает искать подходящую оператор-функцию. Так как в глобальном пространстве имен такой функции нет, то компилятор осуществляет поиска в тех пространствах имен, к которым принадлежат аргументы. В данном случае таким пространством имен будет стандартное пространство имен std:: . В этом пространстве имен определено много таких операторов-функций для перегруженного оператора << . Но ни один из них не имеет в качестве второго параметра параметр с типом std::ostream. Тогда компилятор ищет функции преобразования, которые позволят преобразовать объект std::ostream в объект, для типа которого определен перегруженный оператор <<. И такая функция находится! В классе std::basic_ios, который является базовым для класса std::ostream есть оператор-функция преобразования типа в void *. Мы часто пользуемся этой функцией, даже не задумываясь, что именно она используется, например, в конструкциях, подобных следующей: [pre2]std::vector<int> v; int x; while ( std::cin >> x ) v.push_back( x );[/pre2] Здесь правда вместо std::ostream используется объект std::cin, принадлежащий типу std::istream, но суть не меняется, так как для обоих типов определена данная оператор-функция преобразования в void *. А для типа void * в классе std::ostream определена оператор-функция для оператора <<. Итак, когда имеется предложение вида std::cout << std::cout;, то компилятор неявно преобразует правый объект std::cout в объект типа void *, а далее использует оператор-функцию << для вывода объекта void *. Казалось бы на этом можно поставить точку. Но действительно ли мы можем сказать, что выведется на консоль? Увы, мы не можем это сказать, даже зная то, какие функции вызываются! Дело в том, что стандарт не оговаривает, какое значение должна возвращать оператор-функция преобразования в тип void *. Например, компилятор Borland C++ Builder 5.0 в случае отсутствия ошибки в потоке возвращает значение ( void * ) 1. В то время как, например, компилятор MS VC++ 2010 возвращает ( void * ) this. Но я вас удивлю еще больше, когда скажу. что может быть вообще ничего не выведено на консоль! Дело в том, что значение, возвращаемое оператор-функцией void *, зависит от состояния потока! Если при работе потока произошли ошибки, или, например, был достигнут конец потока (состояние eof), то эта оператор-функция преобразования в тип void * возвращает null. Вот как эта оператор-функция выглядет в MS VC++ 2010 [pre2]operator void *() const { return ( fail() ? 0 : ( void * )this ); }[/pre2] А перед выводом в поток, всегда проверяется состояние потока. И если поток содержит ошибки, то вывод игнорируется. Поэтому в общем случае вы не только не можете сказать, какое значение будет выведено на консоль при выполнении предложения std::cout << std::cout;, но даже не сможете сказать, а будет ли вообще что-то выведено! В качестве подтверждения сказанного можете запустить следующий тестовый пример [pre2]#include <iostream> int main() { std::cout << std::cout; // вывод результата преобразования std:;cout в void * std::cout.setstate( std::ios_base::badbit ); std::cout << std::cout; // отсутствия вывода std::cout.clear(); }[/pre2]

Ответов - 0



полная версия страницы