2015-03-27 17 views
12

Sorprendentemente il codice seguente viene compilato ed eseguito senza errori su una varietà di compilatori e versioni.Perché compila endl (std :: cout)

#include <iostream> 

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

Ideone link

Come si compila? Sono sicuro che non v'è alcun endl di portata globale, perché un codice come

std::cout << endl; 

fallirebbe se non using viene utilizzato, altrimenti è necessario std::endl.

+0

Simile a [Comportamento interessante del compilatore con spazi dei nomi] (http://stackoverflow.com/q/25976267/1708801) –

risposta

23

Questo comportamento è chiamato argument dependent lookup o ricerca Koenig. Questo algoritmo indica al compilatore non solo di esaminare l'ambito locale, ma anche gli spazi dei nomi che contengono il tipo dell'argomento mentre cercano la chiamata di funzione non qualificata.

Per esempio:

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 
} 

Circa il pezzo di codice di cui in oggetto testo:

endl o std::endl è dichiarata in std namespace as following:

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

o

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

E cout o std::cout è declared as

extern std::ostream cout; 

Quindi chiamando std::endl(std::cout); è perfettamente bene.

Ora, quando si chiama solo endl(std::cout);, perché il tipo di argomento cout è da std namespace, non qualificata presumibilmente una funzione endl viene cercato in std spazio dei nomi e si è trovato con successo e ha confermato di essere una funzione e quindi una chiamata alla funzione di qualificato std::endl è fatto.


Ulteriori approfondimenti:

  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

E 'il modo in cui i manipolatori del flusso di lavoro. I manipolatori sono funzioni che passano all'operatore < < come argomenti. Quindi all'interno dell'operatore vengono semplicemente chiamati.

in modo da avere la funzione dichiarata come

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

e si passa il suo puntatore all'operatore < <. E dentro l'operatore che ha dichiarato qualcosa di simile

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

la funzione è called.like

return (*endl)(*this); 

Così quando si vede registrare

std::cout << std::endl; 

poi std::endl è puntatore a funzione che viene passata al operator << come argomento.

Nel record

std::endl(std::cout); 

prefisso del namespace prima del nome endl può essere omessa perché in questo caso il compilatore utilizzare l'argomento di ricerca dipendente. Quindi questo record

endl(std::cout); 

verrà compilato correttamente.

Tuttavia, se per racchiudere il nome della funzione in parentesi poi ADL non viene utilizzato e il seguente record

(endl)(std::cout); 

non verrà compilato.

+1

Penso che la domanda riguardi perché 'std ::' non è richiesto nel modulo di chiamata di funzione –

+0

@Matt McNabb Ho riletto il post e sembra che tu abbia ragione :) –