2015-03-27 3 views
12

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

#include <iostream> 

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

Ideone link

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

std::cout << endl; 

потерпит неудачу, если using не используется или еще вам нужно std::endl.

+0

Похоже на [Интересное поведение компилятора с пространствами имен] (http://stackoverflow.com/q/25976267/1708801) –

ответ

23

Это поведение называется argument dependent lookup или поиск 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 имен as following:

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 является declared as

extern std::ostream cout; 

Так вызова std::endl(std::cout); это прекрасно.

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


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

  1. GOTW 30: Name Lookup

  2. Why does 'std::endl' require the namespace qualification when used in the statement 'std::cout << std::endl;", given argument-dependent lookup?

2

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

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

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

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

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

функция called.like

return (*endl)(*this); 

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

std::cout << std::endl; 

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

В записи

std::endl(std::cout); 

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

endl(std::cout); 

скомпилируется успешно.

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

(endl)(std::cout); 

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

+1

Я думаю, что вопрос в том, почему 'std ::' не требуется в форме вызова функции –

+0

@Matt McNabb Я перечитываю сообщение, и кажется, что вы правы. :) –

Смежные вопросы