2010-12-06 9 views
4

Der folgende Code druckt:Mixing Template-Funktion Überlastung und Vererbung

generic 
overload 

Aber was ich wollte, ist, dass die Überlastung oder die Spezialisierung in beiden Fällen genannt wurden, nicht die generische ein. Ich versuche nicht, Überladung mit Template-Spezialisierung zu mischen, sie sind hier zusammen, weil keiner so funktioniert hat, wie ich es erwartet habe. Gibt es irgendeine Vorlage Magie, um dies zu erreichen?

#include <iostream> 

class Interface {}; 
class Impl: public Interface {}; 

class Bar 
{ 
public: 
    template<typename T> void foo(T& t) { 
     std::cout << "generic\n"; 
    } 
    void foo(Interface& t) { 
     std::cout << "overload\n"; 
    } 
}; 
template<> void Bar::foo<Interface>(Interface& t) { 
    std::cout << "specialization\n"; 
} 

int main() { 
    Bar bar; 
    Impl impl; 
    Interface& interface = impl; 
    bar.foo(impl); 
    bar.foo(interface); 
    return 0; 
} 

Antwort

5

Zwei Möglichkeiten mit type_traits testen, ob das Argument von Interface abgeleitet ist.

#include <boost/type_traits.hpp> 

class Interface {}; 
class Impl: public Interface {}; 

class Bar 
{ 
    template <class T> void foo_impl(T& value, boost::false_type) 
    { 
     std::cout << "generic\n"; 
    } 
    void foo_impl(Interface& value, boost::true_type) 
    { 
     std::cout << "Interface\n"; 
    } 
public: 
    template<typename T> void foo(T& t) { 
     foo_impl(t, boost::is_base_of<Interface, T>()); 
    } 

}; 

Oder deaktivieren Sie die Vorlage, wenn die Bedingung erfüllt ist und nur das Nicht-Template als Kandidat übriglässt.

#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits.hpp> 

class Interface {}; 
class Impl: public Interface {}; 

class Bar 
{ 
public: 
    template<typename T> 
    typename boost::disable_if<boost::is_base_of<Interface, T>, void>::type foo(T& t) 
    { 
     std::cout << "generic\n"; 
    } 

    void foo(Interface&) 
    { 
     std::cout << "Interface\n"; 
    } 
}; 
2

Um die spezielle Funktion zu nutzen, müsste der Compiler einen Parameter Umwandlung &Impl-&Interface zu tun. Wenn nach einer Übereinstimmung der Funktionssignaturen gesucht wird, werden exakte Übereinstimmungen denen vorgezogen, die eine Konvertierung erfordern. Da das generische foo<T> eine exakte Übereinstimmung ist, gewinnt es sowohl gegenüber der Überlast- als auch der spezialisierten Funktion.

0

Die Template-Definition erlaubt eine Funktion erstellt werden:

void Bar::foo<Impl>(Impl& t) 

, die eine bessere Übereinstimmung als die ist, dass Sie das einen Interface& Parameter nehmen definiert.

Sie müssen die übergeordnete Klasse Funktion eine bessere Übereinstimmung machen, vielleicht so:

class Bar 
{ 
    struct fallback { fallback(int) {} }; 
    template<typename T> void foo(T& t, fallback) { 
     std::cout << "generic\n"; 
    } 
    void foo(Interface& t, int) { 
     std::cout << "overload\n"; 
    } 
public: 
    template<typename T> void foo(T& t) { 
     foo(t, 0); 
    } 
}; 

Scheint nicht wirklich obwohl zu arbeiten, sieht http://ideone.com/IpBAv

So würden Sie in eine Typprüfung benötigen Die generische Version sucht nach Unterklassen von Interface.

+0

Als ich diesen Code zum ersten Mal anschaute, dachte ich, er sei aus den gleichen Gründen schlecht geformt, weil der GCC denkt, er sei schlecht geformt. Aber auf einen zweiten Gedanken, ich denke, dass dies auf das Nicht-Template auflösen sollte, weil Nicht-Template-Funktionen eine bessere Übereinstimmung als Funktion Template Spezialisierungen sind, wenn alles andere gleich ist –

+0

@Johannes: Kennen Sie eine Lösung? –

+0

Nein, bitte ignorieren Sie meinen obigen Kommentar: Es ist nicht wahr, denn "wenn alles andere gleich ist" sollte sagen "wenn das Nicht-Template keine schlechtere Konvertierung für einen seiner Parameter hat und das Template keine bessere Konvertierung für einen seiner Parameter hat ". So, wie mein allererster Kommentar sagte, ist dies ein Kreuz: http://stackoverflow.com/questions/3519282/why-is-ist-ambiguity-here/3525172#3525172, und GCC ist korrekt. –