Templates, Vererbung, Syntax und MSVC vs. Clang

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
kaiserludi
Establishment
Beiträge: 467
Registriert: 18.04.2002, 15:31

Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von kaiserludi »

Code: Alles auswählen

template<typename Etype>
class FooBase
{
protected:
	Etype mBar;
};

template<typename Etype>
class Foo : public FooBase<Etype>
{
public:
	Etype getBar(void);
};

template<typename Etype>
Etype Foo<Etype>::getBar(void)
{
	return mBar;
}
Das kompiliert unter MSVC wie erwartet ohne Probleme.
Clang hingegen schmeißt die Fehlermeldung "use of undeclared identifier mBar".

Bug im Clang oder lässt MSVC mir was durchgehen, was eigentlich nicht standardkonform ist? Und vor allem. Wie bringe ich Clang in diesem Fall bei, was ich von ihm will?
Ich könnte natürlich einen protected getter in FooBase hinzufügen, auf den Foo dann zugreift, und die Variable selbst private machen (was sicher auch in punkto Softwaredesign die bessere Wahl ist), aber das erklärt auch nicht, warum der obige Code nicht im Clang kompiliert.

EDIT: OK, könnte ich doch nicht. Foo kennt anscheinend nicht mal public Methoden von FooBase. WTF? Bin ich blind und übersehe hier etwas ganz Banales?


EDIT 2:
Folgender Code kompiliert auch im Clang:

Code: Alles auswählen

template<typename Etype>
class FooBase
{
protected:
	Etype mBar;
};

template<typename Etype>
class Foo : public FooBase<Etype>
{
public:
	Etype getBar(void);
};

template<typename Etype>
Etype Foo<Etype>::getBar(void)
{
	return FooBase<Etype>::mBar;
}
Warum muss ich denn die Basisklasse denn hier noch mal explizit bei jedem Zugriff auf ihre Member aus einer Subklassenmethode heraus davor schreiben?

EDIT3:
Sehe gerade, der Code mit return FooBase<Etype>::mBar kompiliert auch nur solange man das Template nirgends instanziert (logisch: mBar ist ja nicht static)
return this->mBar hingegen passt.
Zuletzt geändert von kaiserludi am 08.03.2012, 14:13, insgesamt 1-mal geändert.
"Mir ist auch klar, dass der Tag, an dem ZFX und Developia zusammengehen werden der selbe Tag sein wird, an dem DirectGL rauskommt."
DirectGL, endlich ist es da
:)

"According to the C++ standard, it's "undefined". That's a technical term that means, in theory, anything can happen: the program can crash, or keep running but generate garbage results, or send Bjarne Stroustrup an e-mail saying how ugly you are and how funny your mother dresses you." :shock:[/size]
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von BeRsErKeR »

Müsste ein Bug im Clang sein. Der erste Code sollte standardkonform sein.

Hast du mal probiert das ganze inline zu machen? Also getBar direkt in der Klasse zu definieren? Manche Compiler hatten/haben mit externen Template-Methoden-Definitionen Probleme. Auch das Visual Studio hatte da wenigstens bis vor ein paar Versionen noch Probleme mit bestimmten Methoden (z.B. Klassentemplate + Methodentemplate).
Ohne Input kein Output.
kaiserludi
Establishment
Beiträge: 467
Registriert: 18.04.2002, 15:31

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von kaiserludi »

Gerade jetzt auf deinen Vorschlag hin ausprobiert, die Methode direkt in der Klasse zu definieren: Macht keinen Unterschied - mit this-> kompiliert es ebenfalls, ohne schmeißt es den gleichen Fehler wie bei separater Definition.
"Mir ist auch klar, dass der Tag, an dem ZFX und Developia zusammengehen werden der selbe Tag sein wird, an dem DirectGL rauskommt."
DirectGL, endlich ist es da
:)

"According to the C++ standard, it's "undefined". That's a technical term that means, in theory, anything can happen: the program can crash, or keep running but generate garbage results, or send Bjarne Stroustrup an e-mail saying how ugly you are and how funny your mother dresses you." :shock:[/size]
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von CodingCat »

Das ist vollkommen korrektes Verhalten von Clang. Dass das in MSVC kompiliert, liegt nur an der unterirdischen MSVC Template-Implementierung, die sämtliche Namen erst bei der Instantiierung auflöst (und vorher einfach alles ignoriert, du könntest fehlerlos jeden Müll in nicht instantiierte Templates schreiben). Laut Standard muss die Namenszuordnung für nicht abhängige Namen auch vor der Instantiierung bereits eindeutig bestimmt sein. Ohne this ist nicht klar, dass dein mBar Teil der noch nicht instantiierten und somit unbekannten Basisklasse FooBase ist, folglich ist mBar klar undefiniert. Mit this-> machst du mBar dagegen zu einem abhängigen Namen, der erst bei Instantiierung aufgelöst wird.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
kaiserludi
Establishment
Beiträge: 467
Registriert: 18.04.2002, 15:31

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von kaiserludi »

Oha. Wieder was gelernt.
Das Imperium dankt.
"Mir ist auch klar, dass der Tag, an dem ZFX und Developia zusammengehen werden der selbe Tag sein wird, an dem DirectGL rauskommt."
DirectGL, endlich ist es da
:)

"According to the C++ standard, it's "undefined". That's a technical term that means, in theory, anything can happen: the program can crash, or keep running but generate garbage results, or send Bjarne Stroustrup an e-mail saying how ugly you are and how funny your mother dresses you." :shock:[/size]
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von BeRsErKeR »

CodingCat hat geschrieben:Ohne this ist nicht klar, dass dein mBar Teil der noch nicht instantiierten und somit unbekannten Basisklasse FooBase ist, folglich ist mBar klar undefiniert. Mit this-> machst du mBar dagegen zu einem abhängigen Namen, der erst bei Instantiierung aufgelöst wird.
Für mich ist das ziemlich unlogisch. Wieso wird da vor der Instantiierung überhaupt was aufgelöst? Und selbst wenn: Der Compiler wird doch wohl so clever sein und die Basisklasse finden. Und zwar dann wenn er die verwendeten Namen auflöst. Dann sollte er doch auch sehen, dass in der Basisklasse mBar enthalten ist.

Entweder ich mach es wie MSVC und gucke erst nach, wenn ich es benutze oder ich löse alles schon zur Compilezeit auf. Aber nicht so ein Zwischending, dass ich einiges versuche aufzulösen ohne aber Basisklassen aufzulösen.
Ohne Input kein Output.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von CodingCat »

Für mich ist das ziemlich unlogisch. Wieso wird da vor der Instantiierung überhaupt was aufgelöst?
Eben damit du nicht irgendwann 2 Monate später bei einer bestimmten Instantiierung den ersten Fehler von deinem Template siehst, nur weil sich damals jemand verschrieben hat, wie das aktuell bei MSVC der Fall ist. Damit sind Templates im alltäglichen Gebrauch oftmals nur unwesentlich hilfreicher als Makros.
Und selbst wenn: Der Compiler wird doch wohl so clever sein und die Basisklasse finden. Und zwar dann wenn er die verwendeten Namen auflöst. Dann sollte er doch auch sehen, dass in der Basisklasse mBar enthalten ist.
Vor der Instantiierung gibt es keine Basisklasse, nur ein Basisklassen-Template. Welche Namen überhaupt existieren, hängt nicht zuletzt von partiellen oder konkreten Spezialisierungen des Basisklassen-Templates ab. Eine Namensauflösung für Elemente etwaiger zukünftiger Basisklassen ist vor der Instantiierung also absolut unmöglich.
Entweder ich mach es wie MSVC und gucke erst nach, wenn ich es benutze oder ich löse alles schon zur Compilezeit auf. Aber nicht so ein Zwischending, dass ich einiges versuche aufzulösen ohne aber Basisklassen aufzulösen.
Das hat durchaus Auswirkungen auf das Laufzeitverhalten. Wir haben hier zufällig einen Fall, in dem keine Namensverdeckung auftritt. Die Regel, unabhängige Namen sofort aufzulösen, macht das Verhalten wesentlich berechenbarer. Werden dagegen alle Namen erst zum Instantiierungszeitpunkt aufgelöst, kann sich je nach Instantiierungskontext (z.B.: überdeckt ein Element der aktuellen Basisklassenspezialisierung eine freie Funktion oder nicht?) das Verhalten auf höchst unberechenbare Weise verändern. Mit this-> ist absolut klar, dass niemals eine freie Funktion gemeint ist. Ohne this-> ist absolut klar, dass immer eine freie Funktion gemeint ist (natürlich nur, sofern diese nicht durch eine offensichtlich existente Methode des Templates selbst verdeckt wird). Auch hier führt das MSVC-Verhalten zu Fehlern, die eigentlich mit Makros der Vergangenheit angehören sollten.

Auf jeden Fall ist das kein Zwischending, sondern sehr konsequent. Es bietet maximale Sicherheit zur Compile-Zeit ohne Einbußen bei der Flexibilität. Das was MSVC macht, ist im Regelfall durch Zufall weitestgehend richtig, aber alles andere als sicher, und manchmal sogar so falsch, dass Programme dadurch kaputt gehen.

Dass das C++-Modulsystem suboptimal und mitunter unintuitiv ist, ist wohl weithin bekannt. Allerdings sollte man der Sprache zu Gute halten, dass Templates auch unglaublich komplex sind. Jede Entscheidung hat weitreichende Konsequenzen, die das Komitee bei näherem Hinsehen meist doch zuverlässig erkannt und berücksichtigt hat, um eventuelle Probleme und unüberschaubare Fehlerquellen bestmöglich zu vermeiden.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
jumphigh
Beiträge: 19
Registriert: 30.06.2004, 13:41
Kontaktdaten:

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von jumphigh »

Das ist Tipp 43 (Abschnitt 7.3) von Scott Meyers "Effective C++".

MfG
Andreas
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: Templates, Vererbung, Syntax und MSVC vs. Clang

Beitrag von BeRsErKeR »

Ok ich verstehe nun worauf du hinaus willst. Ich habe es falsch betrachtet. Danke für die Hinweise.
Ohne Input kein Output.
Antworten