11

Ich versuche, den Code von Sean Elternteil in seinem Vortrag auf GoingNative 2013 präsentiert out - "Inheritance is the base class of evil". (Code aus der letzten Folie erhältlich bei https://gist.github.com/berkus/7041546Warum wählt der Compiler in diesem Fall die falsche Funktionsüberladung?

ich das gleiche Ziel zu erreichen, habe versucht, auf meinem eigenen, aber ich kann‘ t verstehen, warum der folgende Code nicht handeln, wie ich es erwarten.

#include <boost/smart_ptr.hpp> 
#include <iostream> 
#include <ostream> 

template <typename T> 
void draw(const T& t, std::ostream& out) 
{ 
    std::cout << "Template version" << '\n'; 
    out << t << '\n'; 
} 

class object_t 
{ 
public: 
    template <typename T> 
    explicit object_t (T rhs) : self(new model<T>(rhs)) {}; 

    friend void draw(const object_t& obj, std::ostream& out) 
    { 
     obj.self->draw(out); 
    } 

private: 
    struct concept_t 
    { 
     virtual ~concept_t() {}; 
     virtual void draw(std::ostream&) const = 0; 
    }; 

    template <typename T> 
    struct model : concept_t 
    { 
     model(T rhs) : data(rhs) {}; 
     void draw(std::ostream& out) const 
     { 
      ::draw(data, out); 
     } 

     T data; 
    }; 

    boost::scoped_ptr<concept_t> self; 
}; 

class MyClass {}; 

void draw(const MyClass&, std::ostream& out) 
{ 
    std::cout << "MyClass version" << '\n'; 
    out << "MyClass" << '\n'; 
} 

int main() 
{ 
    object_t first(1); 
    draw(first, std::cout); 

    const object_t second((MyClass())); 
    draw(second, std::cout); 

    return 0; 
} 

Diese Version Griffe Druck int in Ordnung, aber nicht im zweiten Fall zu kompilieren, wie der Compiler nicht weiß, wie MyClass zu verwenden, um mit operator<<. Ich kann nicht verstehen, warum der Compiler nicht die zweite Überladung speziell für th auswählen wird e MyClass. Der Code kompiliert und funktioniert gut, wenn ich den Namen der model :: draw() -Methode ändere und den globalen Namespace-Bezeichner :: aus seinem Hauptteil lösche oder wenn ich die globale MyClass-Funktion zu einer vollständigen Template-Spezialisierung ändere.

Die Fehlermeldung I ist wie unten, danach ein paar candidate function not viable... ist

t76_stack_friend_fcn_visibility.cpp:9:9: error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'const MyClass') 
    out << t << '\n'; 
    ~~~^~ 
t76_stack_friend_fcn_visibility.cpp:36:15: note: in instantiation of function template specialization 'draw<MyClass>' requested here 
      ::draw(data, out); 
      ^
t76_stack_friend_fcn_visibility.cpp:33:9: note: in instantiation of member function 'object_t::model<MyClass>::draw' requested here 
     model(T rhs) : data(rhs) {}; 
     ^
t76_stack_friend_fcn_visibility.cpp:16:42: note: in instantiation of member function 'object_t::model<MyClass>::model' requested here 
    explicit object_t (T rhs) : self(new model<T>(rhs)) {}; 
             ^
t76_stack_friend_fcn_visibility.cpp:58:20: note: in instantiation of function template specialization 'object_t::object_t<MyClass>' requested here 
    const object_t second((MyClass())); 
       ^

Warum ist die Template-Version der globalen draw Template-Funktion über die MyClass Funktion Überlastung gewählt? Liegt es daran, dass die Vorlagenreferenz gierig ist? Wie behebe ich dieses Problem?

+0

Ich habe versucht, Ihren Code mit MSVC13 und es kompiliert gut, verwendet die int-Version im ersten Fall und die MyClass-Version mit dem zweiten. Sie sollten infor über Compiler Sie verwenden – Christophe

+0

Ich benutze clang - Version Clang Version 3.5.0 (Tags/RELEASE_350/final). –

+0

[Eine andere Frage mit dem gleichen Prinzip] (http://stackoverflow.com/questions/8501294/different-behavior-for-qualified-and-unqualified-name-lookup-for-template/8501421#8501421) –

Antwort

8

Weil Sie einen qualifizierten Namen im Funktionsaufruf verwenden. [Temp.dep.candidate]:

Für einen Funktionsaufruf, der die Kandidaten Funktionen gefunden die üblichen Suchregeln (3.4.1, 3.4.2 , 3.4.3) unter Verwendung von auf einem Template-Parametern abhängig dass mit Ausnahme von:

  • Für den Teil der Lookup unqualifizierte Namen-Suche (3.4.1) oder qualifizierte Namen-Suche (3.4.3) verwendet, nur Funktionsdeklarationen aus dem Template-Definition Kontext gefunden werden.
  • Für den Teil der Suche, der verknüpfte Namespaces (3.4.2) verwendet, werden nur Funktionsdeklarationen gefunden, die entweder in der Vorlagendefinition oder im Template Instanziierungskontext gefunden werden.

Abschnitt 3.4.2 (alias [basic.lookup.argdep]):

Wenn die Postfix-expression in einem Funktionsaufruf (5.2.2) ist ein unqualifizierte -id, andere Namespaces nicht berücksichtigt während der üblichen unqualifizierten Lookup (3.4.1) kann gesucht werden, und in diesen Namespaces, Namespace-Bereich Freund Funktionsdeklarationen (11.3) nicht anders sichtbar kann gefunden werden.

Also im Wesentlichen gilt ADL nicht, da der Anruf eine qualifizierte-ID verwendet.
Als Barry zeigt in his answer Sie dies, indem Sie den Anruf nicht qualifizierten auflösen kann:

void draw(std::ostream& out) const 
{ 
    using ::draw; 
    draw(data, out); 
} 

Sie haben eine using -Erklärung vor, dass, obwohl hinzuzufügen. Andernfalls findet die Suche nach nicht qualifizierten Namen zuerst die model<>::draw Mitgliedsfunktion, wenn die deklarativen Regionen in aufsteigender Reihenfolge durchsucht werden, und sucht nicht weiter. Aber nicht nur das - weilmodel<>::draw (das ist ein Klassenmitglied ist) ist meine unqualifizierte Namen-Suche gefunden, ADL ist nicht aufgerufen, [basic.lookup.argdep]/3:

X das sein lassen Nachschlagetabelle, die durch unqualifiziertes Nachschlagen (3.4.1) und erzeugt wird, Y sei die Nachschlagetabelle, die durch argumentabhängige Suche (wie folgt definiert) erzeugt wird. Wenn X enthält

  • eine Erklärung eines Klassenmitglied oder
  • eine Block-scope Funktionsdeklaration, die keine using-Deklaration ist oder
  • eine Erklärung, die weder eine Funktion ist, oder eine Funktionsschablone

dann Y ist leer. Sonst ist Y die Menge der Deklarationen, die in den Namespaces gefunden werden, die den -Argumenttypen wie unten beschrieben zugeordnet sind.

Wenn also die using -Erklärung ist die einzige Erklärung durch unqualifizierte Namen-Suche wird die globale draw Vorlage gefunden versehen, die in die deklarative Region model::draw eingeführt wurde. ADL wird dann aufgerufen und findet die später deklarierte draw-Funktion für MyClass const&.

+1

Wissen Sie die Rechtfertigung für den ersten Punkt? –

+0

@SebastianKramer Dies ist das sogenannte "Zweiphasen-Lookup". Betrachte 'void foo (void *); Vorlage Leere Balken (T/* Dummy * /) {foo (0); } void foo (int); int main() {Balken (0); } 'Vermutlich sollte der Autor des Templates' bar' haben, um die 'void *' - Überladung und nicht die 'int'-Überladung aufzurufen. –

+0

@ T.C.Sind Sie sicher, dass die Tatsache, dass es mit der 'using'-Deklaration funktioniert, standardkonform ist? Ich bin ein wenig besorgt über [namespace.udecl]/11 – Columbo

3

Wenn Sie direkt ::draw() anrufen, können Sie ADL nicht korrekt verwenden. (Warum? Ich weiß eigentlich nicht genau und hoffentlich wird jemand kommen und mir das auch erklären [edit: siehe Columbo's answer mit dem Warum]) Aber um tatsächlich ADL zu verwenden, müssen Sie einen unqualifizierten Anruf machen draw wie so:

void draw(std::ostream& out) const 
{ 
    using ::draw; 
    draw(data, out); 
} 

das richtig die Überlastung draw(const MyClass&, std::ostream&) finden.

+0

Erweiterung des Suchbereichs hilft, danke. Ich bin jedoch auf der Suche nach dem Grund für dieses Verhalten. –

+0

@SebastianKramer gut, ADL kann nicht im Definitionskontext angewendet werden, weil wir nicht wissen, was die Argumente oder die Kandidatenfunktionen sind. Und es ist wünschenswert, dass ADL im Instanziierungskontext angewendet wird (aus den gleichen Gründen, dass [ADL außerhalb von Vorlagen wünschenswert ist]) (http://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka) -adl-oder-koenig-Lookup)). Das gibt uns den Status quo. –

+0

Obwohl ich noch nicht verstehe, warum die 3.4.1-Lookups nicht im Instanziierungskontext für einen abhängigen Funktionsaufruf angewendet werden sollten. –

Verwandte Themen