2017-03-09 7 views
9

Ich habe diesen Code:Warum brauche ich in diesem speziellen Fall keine Template-Parameter?

struct Base {}; 

template<typename T> 
struct Foo : Base {}; 

struct Bar { 
    template<typename T> //   v--- What's happening here? 
    Bar(T foo) : baz{std::make_unique<Foo>(foo)} {} 

    std::unique_ptr<Base> baz; 
}; 

Als Überraschung, GCC und Clang akzeptieren und kompiliert. Es scheint, den Vorlagenparameter von Foo abzuleiten, aber es würde keinen Sinn ergeben. Wie kommt es, dass die Compiler akzeptieren, dass, selbst wenn es keine Überladung von std::make_unique gibt, die einen Template-Template-Parameter nimmt? Live example

+3

Err ... versuchen Sie, ein "Bar" -Objekt mit diesem Konstruktor zu erstellen, wenn Sie überhaupt können. Dann sagen Sie uns, ob der Compiler den Code akzeptiert. – WhiZTiM

+0

@WhiZTiM es den Code ablehnen.Ich finde es immer noch merkwürdig, dass sich beide Compiler nicht über den Mismatch-Template-Parameter-Typ beschweren. –

Antwort

8

Es gibt einige Situationen, in denen eine Vorlage immer ungültig ist, unabhängig davon, welche Vorlage Argumente geliefert werden, aber der Compiler ist nicht in der Lage, dass, um herauszufinden, weil sie nicht über die Fähigkeit, jeden versuchen ersetzen möglicher Satz von Vorlagenargumenten. Gemäß dem Standard ([temp.res]/8):

Wenn keine gültigen Spezialisierungs für eine Vorlage erzeugt werden, und das Template nicht instanziiert wird, ist die Vorlage schlecht ausgebildet ist, erforderlich ist keine diagnostischen .

Das bedeutet, dass der Compiler intelligent sein darf und beweisen, dass es keine gültige Spezialisierung ist, und ein Übersetzungsfehler erzeugen, aber es wird auch zu sein, nicht intelligent genug, erlaubt, und kein Übersetzungsfehler zu erzeugen. Nachdem die Vorlage instanziiert wurde, muss der Compiler den Fehler natürlich diagnostizieren.

Es ist nicht illegal, den Namen einer Vorlage ohne Vorlagenargumente zu verwenden. Es gibt einige Umstände, unter denen der Compiler Argumente herleiten kann. Zum Beispiel:

template <class T> 
void foo(T x); 

int main() { 
    void (*p)(int) = foo; // p points to foo<int> 
} 

In Ihrem Code, stellt sich heraus, dass Sie Foo in einem Kontext verwendet habe, in dem die Vorlage Argumente können nicht abgeleitet werden. Wenn die Compiler klüger wären, hätten sie das herausgefunden. Aber die Tatsache, dass sie es nicht geschafft haben, es herauszufinden, bedeutet nicht, dass Ihr Code korrekt ist.

+1

Schönes Code-Snippet, hatte keine Ahnung, dass ein solcher Code gültig ist und der Template-Parameter kann in einem solchen Kontext abgeleitet werden. – vsoftco

4

Der Grund, warum dies funktioniert, ist, weil C++ nicht wirklich Vorlagenfunktionen erstellt, bis sie von irgendwo im Code aufgerufen werden. In diesem Fall, da Ihr Code nicht versucht, eine Bar zu erstellen, wird Ihre Vorlage ein Arg--Konstruktor nie generiert, so dass der Compiler nie überprüfen muss, ob der Code korrekt ist.

Vorlagenfunktionen werden vom Compiler generiert, sobald sie verwendet werden, und sie werden nur basierend auf den an sie übergebenen Typen generiert. Also, wenn Sie versuchen, eine Bar wie so zu erstellen:

Foo<int> f; 
Bar b = Bar(f); 

Der Compiler wird versuchen, den Konstruktor zu erzeugen, und mit einem Fehler fehl wie:

error C2955: 'Foo' : use of class template requires template argument list 

Weil es jetzt weiß der Code falsch ist.

Wenn Sie darüber nachdenken, muss es so funktionieren: Da C++ keine Einschränkungen für Schablonentypen zulässt, müsste der Compiler alle möglichen Kombinationen von Schablonentypen ausprobieren, um herauszufinden, ob es sich um eine Vorlage handelt Funktion hat Syntaxfehler.

+0

Der Compiler führt auch einige Vorlagenüberprüfungen durch, aber in diesem Fall wird tatsächlich keine Überprüfung durchgeführt. Die Regeln sind eigentlich ziemlich kompliziert, siehe [C++ Templates: Eine vollständige Anleitung] (https://www.amazon.com/Templates-Complete-Guide-David-Vandevoorde/dp/0201734842) für eine detaillierte Erklärung. – vsoftco

+0

Ja, Sie können jeden Template-Parameter einschränken, indem Sie das Konzept über sfinae emulieren. –

+1

@GuillaumeRacicot Damit können Sie die Typen, die zum Instanziieren einer Vorlagenfunktion verwendet werden können, nicht einschränken. Es ermöglicht Ihnen nur, Syntaxfehler in bestimmten Überladungen zu erstellen, so dass dieser Instanziierung niedrigere Priorität in der Überladungsauflösung gegeben wird. Jeder Typ kann weiterhin für jeden Schablonentyp verwendet werden, sodass der Compiler immer noch einen beliebigen Typ als möglichen Kandidaten berücksichtigen muss. Dies macht es unmöglich, Syntaxfehler zu finden, bevor eine bestimmte Instanziierung gewählt wird, wie ich bereits sagte. –

Verwandte Themen