2015-03-27 28 views
12

Überraschenderweise kompiliert der untenstehende Code und läuft ohne Fehler auf einer Vielzahl von Compilern und Versionen.Warum kompiliert endl (std :: cout)

#include <iostream> 

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

Ideone link

Wie funktioniert es kompilieren? Ich bin sicher, dass es keine endl in globalen Bereich ist, weil ein Code wie

std::cout << endl; 

würde scheitern, wenn using verwendet wird, oder sonst noch brauchen std::endl.

+0

Ähnlich wie [Interessantes Verhalten des Compilers mit Namespaces] (http://stackoverflow.com/q/25976267/1708801) –

Antwort

23

Dieses Verhalten wird argument dependent lookup oder Koenig-Lookup genannt. Dieser Algorithmus weist den Compiler an, nicht nur den lokalen Bereich zu betrachten, sondern auch die Namespaces, die den Argumenttyp enthalten, während er nach dem Funktionsaufruf unqualifiziert sucht.

Für 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 
} 

Über das Stück Code in Frage Text genannt:

endl oder std::endl in std Namespace deklariert ist as following:

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

oder

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

Und cout oder std::cout ist declared as

extern std::ostream cout; 

So std::endl(std::cout); Aufruf völlig in Ordnung ist.

Nun, wenn man nur Anrufe endl(std::cout);, weil die Art der Argumentation cout von std namespace ist, unqualifizierte angeblich eine Funktion endl in std Namensraum gesucht wird, und es wird festgestellt, erfolgreich und bestätigte std::endl eine Funktion und damit ein Aufruf an qualifizierter Funktion zu sein ist gemacht.


Weiterführende Literatur:

  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

Es ist die Art und Weise der Strom Manipulatoren arbeiten. Manipulatoren sind Funktionen, die an den Operator < < als Argumente übergeben wurden. Dann werden sie im Operator einfach gerufen.

So haben Sie Funktion erklärt wie

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

und Sie übergeben ihre Zeiger auf Operator < <. Und im Inneren der Operator, der etwas erklärt wie

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

die Funktion called.like

return (*endl)(*this); 

So bei der Aufnahme sehen

std::cout << std::endl; 

dann std::endl Funktionszeiger, die auf die übergeben wird operator << als Argument.

Im Rekord

std::endl(std::cout); 

Namespacepräfix vor Namen endl da in diesem Fall verzichtet werden kann der Compiler wird das Argument abhängigen Lookup verwenden. Dieser Datensatz

endl(std::cout); 

wird erfolgreich kompiliert.

Allerdings, wenn Sie die Funktionsnamen in Klammern einzuschließen dann wird ADL nicht verwendet und die folgende Datensatz

(endl)(std::cout); 

wird nicht kompiliert.

+1

Ich denke, die Frage ist, warum 'std ::' nicht auf der Funktion-Aufruf-Formular erforderlich ist –

+0

@Matt McNabb Ich las den Beitrag erneut und es scheint, dass Sie Recht haben. :) –

Verwandte Themen