Почему endl(std::cout) компилируется
Удивительно, но приведенный ниже код компилируется и работает без ошибок на различных компиляторах и версиях.
#include <iostream>
int main() {
endl(std::cout);
return 0;
}
Как это компилируется? Я уверен, что нет endl
в глобальном масштабе, потому что такой код
std::cout << endl;
потерпит неудачу, если using
используется или вам нужно std::endl
,
2 ответа
Такое поведение называется поиском, зависящим от аргумента, или поиском Кенига. Этот алгоритм говорит компилятору не только смотреть на локальную область видимости, но также на пространства имен, которые содержат тип аргумента, при поиске неквалифицированного вызова функции.
Например:
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
пространство имен следующим образом:
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
сделан.
Дальнейшее чтение:
Это способ работы потоковых манипуляторов. Манипуляторы - это функции, которые передаются оператору << в качестве аргументов. Тогда внутри оператора они просто называются.
Итак, у вас есть функция, объявленная как
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
и вы передаете указатель на оператор <<. И внутри оператора, который объявил что-то вроде
ostream& ostream::operator << ( ostream& (*op)(ostream&));
функция называется.like
return (*endl )(*this);
Таким образом, когда вы видите запись
std::cout << std::endl;
затем std::endl
указатель функции, который передается в operator <<
в качестве аргумента.
В записи
std::endl( std::cout );
префикс пространства имен перед именем endl
может быть опущено, потому что в этом случае компилятор будет использовать Argument Dependent Lookup. Таким образом, эта запись
endl( std::cout );
успешно скомпилируется.
Однако если заключить имя функции в круглые скобки, тогда ADL не используется и следующая запись
( endl )( std::cout );
не будет скомпилировано.