Почему компилятор endl (std:: cout)
Удивительно, что приведенный ниже код компилируется и работает без ошибок в различных компиляторах и версиях.
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
Идеальная ссылка
Как он компилируется? Я уверен, что в глобальной области нет endl
, потому что такой код, как
std::cout << endl;
завершится с ошибкой, если не используется using
или вам нужно std::endl
.
Ответы
Ответ 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 );
не будет скомпилирован.