On-line: гостей 0. Всего: 0 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 28.07.12 19:04. Заголовок: Что будет выведено на консоль предложением std::cout << std::cout;


Этот вопрос я встретил на одном форуме, на котором там же и ответил. Но я решил продублировать здесь этот вопрос и дать более полный ответ на него, так как это познавательно.
Итак, что будет выведено на консоль в результате выполнения предложения

std::cout << std::cout;


На первый взгляд такая запись вообще выглядет бессмысленной, и кто-то даже может предположить, что возникнет ошибка компиляции. Каково же будет его удивление, когда он обнаружит, что код компилируется!

Мы настолько привыкли использовать перегруженный оператор << для вывода на консоль фундаментальных типов или определеннных пользователем типов данных, что предполагаем, что в правой части этого выражения могут присутствовать только такие объекты указанных типов. Например,

int x = 5; 
char c = 'A";
char a[] = "An array.";
std::cttring s( "A string." );

std::cout << x << ", " << c << ", " << a << ", " << s << std::endl;


И даже сами часто определяем свои оператор-функции перегрузки оператора << для собственных типов. Например,

#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;
}


Но какой смысл имеет ниже приведенный оператор?!

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 *. Мы часто пользуемся этой функцией, даже не задумываясь, что именно она используется, например, в конструкциях, подобных следующей:

std::vector<int> v; 
int x;

while ( std::cin >> x ) v.push_back( x );


Здесь правда вместо 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

operator void *() const 
{
return ( fail() ? 0 : ( void * )this );
}


А перед выводом в поток, всегда проверяется состояние потока. И если поток содержит ошибки, то вывод игнорируется.

Поэтому в общем случае вы не только не можете сказать, какое значение будет выведено на консоль при выполнении предложения std::cout << std::cout;, но даже не сможете сказать, а будет ли вообще что-то выведено!

В качестве подтверждения сказанного можете запустить следующий тестовый пример

#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();
}


Спасибо: 0 
ПрофильЦитата Ответить
Новых ответов нет


Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 15
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет