Почему 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 сделан.


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

  1. GOTW 30: поиск имени

  2. Почему для 'std::endl' требуется квалификация пространства имен при использовании в операторе 'std::cout << 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 );

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

Другие вопросы по тегам