2017-08-09 4 views
-4

So vor kurzem mir ein Problem mit MSVC gestoßen, wo es würde meinen Code nicht kompiliert, nach MSVC Entwickler-Community dieser Linie war schuld (Übersetzungsfehler in Header wie <cmath> und <type_traits>Unterschied zwischen Typnamen und typename1 C++

#define type typename //because type T looks infinitely better than typename T 
verursacht

der (vermeintlichen) fix war:

#define type typename1 

das Problem ist, ich auf ‚typename1‘ keine Unterlagen finden können, was der Unterschied zwischen ihnen ist, warum typename1 nicht mit Hunderten von Fehlern irren out verursachen wenn typname tut das? Ist es nur eine schlechte Übung, einen Alias ​​für typename zu definieren, kann ich mir nicht vorstellen, wie es wäre, aber vielleicht?

Um auf die Frage zurückzukommen, wie unterscheiden sich Typname und Typname1 und warum bricht dieser Code ein?

#define type typename //this is supposedly the problem 

#include <cstdint> 
#include <cstdio> 
#include <cmath> //this breaks 
#include <type_traits> //this breaks 
//as would any other header that includes corecrt_math or similar 

//to reproduce the problem we need not include templates, 
//but there would naturally be several since I want to use the 'type' 
//identifier 

int main() 

{ 

    return 0; 

} 
+4

'typename' ist ein Schlüsselwort. Wenn es an Stellen erscheint, an denen es nicht erscheinen sollte, werden Fehler verursacht. Dies ist einer der Gründe, warum Makros vermieden werden sollten. – NathanOliver

+2

Der [typenname] (http://en.cppreference.com/w/cpp/keyword/typename) ist ein reserviertes Schlüsselwort in C++. _typename1_ ist nicht. Bevorzugen Sie 'const' oder' constexpr' um 'define' zu ​​definieren. – Ron

+1

In 'C++' möchten Sie wahrscheinlich nicht #define verwenden. – drescherjm

Antwort

1

TL; DR: Sie versuchen einen Typalias durch Makrosubstitution. Das ist immer riskant. Die Lösung besteht darin, das Makro with a using declaration zu ersetzen.Zum Beispiel

using myBigInteger = long long int; 

Allgemeine Form:

using <alias> = <data type> 

Kompliziertere Beispiele:

using datavector = std::vector<int32_t>; // a vector of 32 bit ints 
using dataiterator = datavector::iterator; // and iterator of a vector of 32 bit ints 
using functionpointer = void (*)(dataiterator begin, dataiterator end); 
// a pointer to a function that does something to the specified iterator range 

Die oben gibt gute One-Stop-Ort, um die Art der möglicherweise Tausende von Stellen im Code ändern wo std::vector<int32_t> in std::vector<double> geändert werden muss, aber ein einfacher finden und ersetzen wird nicht tun, weil nur einige der std::vector<int32_t> s, die vector s voller Daten, muss geändert werden.

Nächstes verwenden Sie nicht typename

using type = typename; 

Dies wird genauso schlecht ausfallen, aber anders, weil Typname ein reservierter Begriff mit besonderen Bedeutungen kontextabhängig ist. Normalerweise würden Sie etwas entlang der Linien von

error: ‘typename’ does not name a type

Aber in diesem Fall Typname wird verwendet, um declare dependent, nested, naming, so dass der schlechte Compiler geht der Suche nach dieser Verschachtelung erwarten und kann es nicht finden. Eine Komödie von Fehlermeldungen kann folgen.

Lange Version und warum das angegebene Codebeispiel fehlschlägt:

Zuerst ein wenig hilfreichen Lesung: How does the compilation/linking process work?

Vor dem Kompilieren des C++ Präprozessor (und der C-Präprozessor für diese Angelegenheit) beginnt machen eine umfangreiche Datei der C++ - Quelldatei und alle darin enthaltenen Header. Gleichzeitig führt der Prozessor eine Textersetzung von Tokens durch, für die eine Substitution definiert wurde. Dies bedeutet, dass jedes Mal, wenn das Token type gefunden wird, es durch das Token typename ersetzt wird. Es spielt keine Rolle, welche Datei enthält type nach

#define type typename 

gefunden wird. Alle Instanzen von type werden in Ihren Dateien, den Standardbibliotheksheadern, Drittanbieterheadern, alles ersetzt, da es sich jetzt um eine einzige große Datei handelt.

Nachdem der Präprozessor das Sammeln und Konvertieren aller erforderlichen Komponenten abgeschlossen hat, wird der Compiler auf dieser einen großen Datei ausgeführt. In diesem Fall findet es den reservierten Term typename verstreut durch die Datei an Stellen, die absolut keinen Sinn machen und es dementsprechend Fehlermeldungen ausspuckt. In diesem Fall, während typename schlecht ist, ist praktisch jeder Name schlecht, weil die Textersetzung absolut sinnlos ist. Es ist egal, wie der Token verwendet wird. Das Token wird ersetzt.

#define type fubar 

wird Kennung type zu fubar umbenennen, die bereits mit anderen Bezeichnern in Konflikt stehen könnten fubar benannt. Die Leser des Codes werden keine Ahnung haben, was vor sich geht. Ich habe mich einmal selbst in den Fuß mit einer Variablen namens strlen geschossen, weil die Bibliotheksfunktion strlen als Makro implementiert wurde. Zeitverschwendung Stunden durch die bizarro Fehlermeldungen, die verursacht.

Hinweis Sie können eine bestimmte Menge an Erfolg finden, indem Sie einfach die #define verschieben, nachdem alle Header enthalten sind, aber die illegale Verwendung von typename wird immer noch ein Auslösepunkt sein.

+0

unter g ++ funktionierte es mit dem zuletzt eingeschlossenen Konfigurationsheader (der die # type-Direktive Typdefine zuletzt anlegt), aber unter MSVC hat es nicht funktioniert, weshalb ich Microsofts Lösung in Frage stellte, um eine Standard-Direktive durch eine Microsoft-Erweiterung zu ersetzen. Nun, nicht, dass es jetzt wichtig ist, habe ich bereits die gesamte Codebasis über die schwarze Magie von Regex refactored mit name = otherName funktioniert wahrscheinlich für meine anderen Makros wie #define Euler vector3 so entweder ich gewonnen habe etwas aus diesem ganzen Debakel. – LordHexahedron

+0

Verwendung von '# define' an Orten, an denen' typedef' (oder neuerdings 'using') funktionieren würde, ist seit mehr als zwanzig Jahren eine schlechte Praxis - es gibt buchstäblich keinen guten Grund, das überhaupt zu tun. – Useless

2

Is it just bad practice to define an alias for typename, I can't imagine how it would be but maybe?

Ja, es ist eine schlechte Praxis Bits der Sprache mit Makros neu zu definieren.

Here ist eine Beschreibung des Schlüsselworts typename. Es ist Standard, gut definiert und überall verwendet. Jeder weiß, was es bedeutet, einschließlich Syntaxhervorhebungs-Editoren und anderer Werkzeuge, ohne den Präprozessor ausführen zu müssen.

Betrachten Sie nun das Token type. Ich kann seine Verbform als Funktionsnamen verwenden. Ich kann es als variablen Namen verwenden. Ich kann es sogar als den Namen eines Typs verwenden. Ich kann all diese Dinge tun, weil es kein Schlüsselwort ist.

Wenn der Präprozessor alle diese Variablen, Funktionen und Typen durch ein Schlüsselwort ersetzt, werden die Dateien unterbrochen. Der C (++) Präprozessor ist ein sehr, sehr stumpfes Instrument zur Anpassung der Sprache, da er den Kontext nicht kennt.

In jedem Fall endet der Versuch, C++ wie eine andere Sprache aussehen zu lassen, selten gut. Die Syntax ist nicht so flexibel und die Makros sind nicht so mächtig.

Die Behauptung, dass type T unendlich schwach ist besser als typename T aussieht. Wenn unendlich besser wirklich bedeutet unendlich mehr wie einige andere Sache mag ich mehr, macht es Sinn, aber es ist immer noch ein schreckliches Argument. Sie schreiben C++ und es sieht aus wie C++. Vorgeben, es ist etwas anderes ist eher zu produzieren schlechten C++ Code als einige ästhetische Nirvana.

+0

ich Ihr Argument verstehen, aber ich denke immer noch, es ist schlechtes Design, die der Präprozessor nicht Zusammenhang bewusst ist, es zu alias Dingen relativ häufig ist, zu vergleichen: vector3 Rotation; mit euler rotation, ja, man könnte euler definieren, aber dann wäre es nicht mehr trivial, mit anderen vector3's zu arbeiten oder auf anderen vector3 damit zu arbeiten. der einzige Grund, warum ich auf eine #define sowieso war, ist, weil die Alternativen nicht funktionieren, nun, ich schätze, es spielt keine Rolle - ich werde nur marginal meinen Code verwenden und typename verwenden, bedeutet Ihre Erklärung, dass es keine andere vernünftige Lösung gibt . – LordHexahedron

+0

Der Präprozessor wird vor dem Parsing aufgerufen. Daher kann der Code nicht sinnvoll analysiert werden, um den Kontext abzuleiten. Eine andere Sprache mit einfacherer Grammatik könnte ein besseres Makro-System haben, aber es wäre kein Pre-Prozessor mehr. Wie auch immer, dieses Zeug hat sich seit Jahrzehnten nicht verändert, also sollte es nicht wirklich überraschen. Das Beispiel in Ihrem Kommentar scheint wie ein 'using' oder' typedef' zu sein. – Useless

Verwandte Themen