Der folgende Code kompiliert in GCC (ich habe ideone verwendet, das gcc-4.3.4 verwendet), kompiliert aber nicht in Visual Studio. Ist es Standard-Code und ein Fehler in Visual C ++ 2008 und 2010 (ich habe es in beiden versucht) oder nicht-Standard-und GCC ist glücklich, es zu kompilieren?
%Vor%Fehler 1 Fehler C2039: '{ctor}': ist kein Mitglied von 'Foo' main.cpp 25
Beachten Sie, dass das Problem die Out-of-Line-Definition des Konstruktors von Foo
ist. Wenn Sie es in der Klasse definieren, ist Visual-C ++ glücklich:
Außerdem kompiliert der folgende Code auf beiden (beachten Sie, dass die || und die Logik, nachdem es fehlt):
%Vor%Kredit, wo Kredit fällig ist, dies ist eine leicht modifizierte Version, die mir von Dietmar Kühl gegeben wurde)
Visual C ++ 2008/2010 ist schuld. Aber es kann umgangen werden - in mehr als einer Hinsicht.
Betrachten wir den Typ von Foo<BasePolicy2> fb
.
Diese Deklaration setzt den zweiten Vorlagenparameter der Vorlage Foo & lt; & gt; wie zuerst erklärt. Also ist sein Typ explizit:
%Vor% Wenn Sie bereits zufrieden sind, dass /*1*/
auf Folgendes kocht:
Dann kannst du uns wieder unter Das Rendezvous unten treffen.
Nun, wir können sehen, dass der Typ:
%Vor%löst auf:
%Vor% Als nächstes überprüfen wir, wie template enable_if<>
definiert ist. In namespace cool
haben wir:
also /*4*/
wiederum setzt den zweiten Template-Parameter von template enable_if<>
als Standard, und der Typ dieses Standards ist void
.
Ok, dann /*6*/
spezialisiert template enable_if<>
in Bezug auf seinen zweiten Template-Parameter, wenn der erste bool-Parameter vorhanden ist
Der Wert true
, und es heißt in diesem Fall, dass enable_if<>
eine typedef type
exportieren soll, die den Typ der zweiten Vorlage hat
Parameter. Wenn der erste bool-Parameter false
ist, dann wird dieser typedef nicht existieren und unser Compiler wird barf.
Gut, wir wissen, wenn /*4*/
überhaupt kompiliert wird, dann some_boolean_consant == true
, und der exportierte Typ type
ist der von
der voreingestellte 2. Vorlagenparameter von enable_if<>
. Was ist void
.
Nun habe ich den Typ von:
abgeleitet %Vor% Es ist void
. Also /*1*/
kocht auf /*2*/
herunter, und das ist der voreingestellte Typ von Foo<BasePolicy2>
.
Wie es sich gehört, aber wenn Sie Zweifel an dieser Schlussfolgerung haben, dann fügen Sie dies dem Programm auf globaler Ebene hinzu und kompilieren Sie es:
%Vor%Der Compiler wird sagen:
%Vor%oder Wörter zu diesem Zweck.
Das Wissen, dass /*1/
= /*2/
uns eine gewisse Hebelwirkung auf den Visual C ++ - Kompilierungsfehler gibt, um den es in der Frage geht:
Der Fehler beschwert sich darüber, dass der Konstruktor, der ihn hervorbringt, tatsächlich kein Konstruktor ist, der durch den Typ deklariert ist, zu dem er gehören muss, d
Foo<Policy>::Foo()
ist kein Konstruktor von Foo<Policy>
.
Nun wird in der Definition von Foo<Policy>
standardmäßig der zweite Template-Parameter der ursprünglichen Deklaration angegeben, von dem wir wissen, dass er void
sein muss. Also die
Frage stellt sich vor: Beachtet der Compiler tatsächlich diesen voreingestellten zweiten Template-Parameter? - d. h. erkennt es, dass die
Template-Definition von Foo<Policy>
ist eine Template-Definition von Foo<Policy,void>
?
Die Antwort lautet Nein. Wenn wir einfach die Definition der Vorlage und ihres Konstruktors so ändern, dass der zweite Standardparameter explizit angegeben wird:
%Vor%dann kompiliert das Programm sauber mit Visual C ++.
Dauert Visual C ++ möglicherweise für seine eigenen - fragwürdigen - Überzeugungen über Standard C ++ an, indem es diesen Fehler anzeigt? Oder ist es nur kaputt? Es ist nur kaputt. Denn wenn wir es auf diesem einfacheren, aber im wesentlichen gleichen Programm versuchen, hat es kein Problem:
%Vor% Dies zeigt, dass es der Mund voll von Template Metaprogramming /*3*/
ist, der die Verdauungsstörung verursacht,
und wenn der Fragesteller darauf hinweist, ob dieser Biss voll und ganz experimentell vereinfacht wird, indem man die Operation ||
löscht,
Alles ist gut (außer dass unsere cool::
Logik natürlich kaputt ist).
Wir haben schon eins gesehen. Setzen Sie den Template-Parameter void
explizit in den Definitionen von Foo<Policy>
und Foo<Folicy>::Foo()
. Sie können jetzt gehen, wenn das alles ist, was Sie wissen wollen.
Aber das juckt. Wir wenden den Fix auf der Ebene /* Simple Case */
an, wenn wir wissen, dass der Fehler nicht auftritt
allgemein auf dieser Ebene. Es ist in der Dunkelheit der Metaprogrammierung des Metaprogrammierens des Compilers, also a
Workaround, der nicht juckt, wäre zumindest auf namespace cool
beschränkt.
Hier ist eine Regel der Vorsicht über Vorlage Metaprogrammierung (TMP) Helfer Vorlagen, die ich hoffe, den Fortschritt von Compiler werden mich bald vergessen lassen: Lass die Blighters nicht im Flug instanziieren . Solche Vorlagen existieren nur um die Kompilierungslogik zu erleichtern. Solange der Compiler diese Logik nur durch rekursives Definieren ausführen kann Arten ist es wahrscheinlich, in seiner Komfortzone zu bleiben (mindestens so lange, wie seine Instanziierungstiefe reicht). Ob es ist verpflichtet, Zwischenformen in der Förderung der Kompilierungslogik zu instantiieren, dann seine seltsamste Peccadilloes neigen dazu, aufzutauchen.
Wenn Sie Ihre TMP-Hilfsklassen so codieren, dass Kompilierungslogik nur durch Übernahme der Werte erreicht werden kann von statischen oder enumerierten Konstanten, die sie aussetzen, dann zwingen Sie den Compiler, die ganze Zeit instanziieren, denn nur so erhalten diese statischen oder enumerierten Konstanten Werte.
Fall in Punkt, Klasse /*3*/
mit seiner mysteriösen toxischen Operation ||
. Die TMP-Hilfsklassen von namespace cool
tun dies
Meta-Logik auf booleschen Konstanten - unnötig.
Der vorsichtige Ansatz für TMP-Hilfsklassen besteht darin, sie so zu definieren, dass logische Operationen alle simuliert werden können, ohne Instanziierung,
durch rekursive Typdefinitionen, wobei Konstanten exportiert werden - damit Sie sie letztendlich brauchen - nur wenn die gesamte Kompilierzeit
Logik ist erfolgreich gelandet. Ein vorsichtiges Neuschreiben des Inhalts von namespace cool
könnte etwa so aussehen:
und die entsprechende Deklaration der Vorlage Foo & lt; & gt; würde so aussehen:
%Vor% Auf diese Weise wird keines unserer cool::
Dinge jemals instanziiert, bis der ganze Shebang ist
instanziiert: Wir beschäftigen uns ausschließlich mit Typen für die gesamte Meta-Logik. Wenn sich das ändert
werden zum Programm gemacht, dann ist die juckende Problemumgehung nicht erforderlich.
Und GCC ist auch damit zufrieden.
Oh. Sollte es nicht sein:
%Vor% (Ohne <Policy>
in der Strukturdeklaration.) Nicht sicher, was der Standard sagt, aber eine Template-Struktur mit dem Template-Parameter deklarieren, nachdem der Strukturname eine partielle Spezialisierung dieser Vorlage deklarieren sollte.
Tags und Links c++ visual-c++ templates