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

Почему компилятор endl (std:: cout)

Удивительно, что приведенный ниже код компилируется и работает без ошибок в различных компиляторах и версиях.

#include <iostream>

int main() {
    endl(std::cout);
    return 0;
}

Идеальная ссылка

Как он компилируется? Я уверен, что в глобальной области нет endl, потому что такой код, как

std::cout << endl;

завершится с ошибкой, если не используется using или вам нужно std::endl.

4b9b3361

Ответ 1

Это поведение называется зависимым от аргумента поиска или поиском Koenig. Этот алгоритм говорит компилятору не просто рассматривать локальную область, но также пространства имен, содержащие тип аргумента, ища вызов функции неквалифицированный.

Для ex:

namespace foo {
  struct bar{
    int a;
  };

  void baz(struct bar) {
    ...
  }
};

int main() {
  foo::bar b = {42};
  baz(b);  // Also look in foo namespace (foo::baz)
  // because type of argument(b) is in namespace foo
}

О фрагменте кода, который содержится в тексте вопроса:

endl или std::endl объявлен в std namespace следующим образом:

template< class CharT, class Traits >
std::basic_ostream<charT,traits>&     endl( std::basic_ostream<CharT, Traits>& os );

или

std::ostream& endl (std::ostream& os);

И cout или std::cout объявлено как

extern std::ostream cout;

Так что вызов std::endl(std::cout); отлично.

Теперь, когда вы вызываете только endl(std::cout);, потому что тип аргумента cout от std namespace, неквалифицирован, предположительно, функция endl выполняется в пространстве имен std, и она найдена успешно и подтверждена как и, таким образом, выполняется вызов квалифицированной функции std::endl.


Дальнейшее чтение:

Ответ 2

Это то, как работают манипуляторы потока. Манипуляторы - это функции, которые передаются оператору < как аргументы. Тогда внутри оператора они просто вызываются.

Итак, у вас есть функция, объявленная как

template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);

и вы передаете его указатель на оператор <. И внутри оператора, который объявил что-то вроде

ostream& ostream::operator << ( ostream& (*op)(ostream&));

функция вызывается. <

return (*endl )(*this);

Таким образом, когда вы видите запись

std::cout << std::endl;

then std::endl - это указатель на функцию, который передается в аргумент operator <<.

В записи

std::endl( std::cout );

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

endl( std::cout );

будет успешно скомпилироваться.

Однако, если заключить имя функции в круглые скобки, то ADL не используется, а следующая запись

( endl )( std::cout );

не будет скомпилирован.