Gibt es einen Grund, warum der Standard sie als Vorlage struct
s anstelle von einfacher boolescher constexpr
? angibt?
In einer zusätzlichen Frage, die wahrscheinlich in einer guten Antwort auf die Hauptfrage beantwortet wird, wie würde man enable_if
stuff mit den Nicht-Struktur-Versionen machen?
Ein Grund ist, dass constexpr
-Funktionen kein verschachteltes type
-Member bereitstellen können, was in einigen Meta-Programmier-Situationen nützlich ist.
Um es klar zu machen, ich spreche nicht nur von Transformationsmerkmalen (wie make_unsigned
), die Typen erzeugen und offensichtlich nicht constexpr
functions gemacht werden können. Alle Typeigenschaften bieten solche verschachtelten type
-Mitglieder, sogar unäre Typeigenschaften und binäre Typeigenschaften . Zum Beispiel is_void<int>::type
ist false_type
.
Natürlich könnte dies mit std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>
umgehen, aber es wäre nicht so praktisch.
In jedem Fall, wenn Sie wirklich Funktion-ähnliche Syntax wollen, ist das bereits möglich. Sie können einfach den Merkmalkonstruktor verwenden und die implizite consExpr-Konvertierung in integral_constant
nutzen:
Für Transformationsmerkmale können Sie einen Vorlagenalias verwenden, um etwas zu erhalten, das dieser Syntax sehr nahe kommt:
%Vor%Beachte: Das sieht eher aus wie eine Tirade als eine richtige Antwort ... Ich hatte aber ein bisschen Juckreiz beim Lesen der vorherigen Antworten, also bitte entschuldigt mich;)
Erstens werden Klassenmerkmale historisch mit Templatestrukturen erstellt, da sie vor constexpr
und decltype
liegen. Ohne diese beiden war es ein wenig mehr Arbeit, Funktionen zu verwenden, obwohl die verschiedenen Bibliotheksimplementierungen von is_base_of
Funktionen intern verwenden mussten, um die Vererbung richtig zu machen.
Was sind die Vorteile für die Verwendung von Funktionen?
typename ::type
sieht dumm TM ) Tatsächlich ist die Vererbung wahrscheinlich der Hauptpunkt gegen Klassenmerkmale. Es ist höllisch ärgerlich, dass du alle abgeleiteten Klassen spezialisieren musst, um momma zu machen. Sehr nervig. Mit Funktionen erben Sie einfach die Eigenschaften und können spezialisieren, wenn Sie möchten .
Was sind die Nachteile ?
Natürlich könnte man argumentieren, dass das eigentlich nervig ist: Spezialisiert auf iterator_traits
, erbt ihr so oft kostenlos von std::iterator_traits
nur , um den Standard zu bekommen. Verschiedene Funktionen würden dies natürlich bereitstellen.
Konnte es funktionieren?
Nun, in einem Wort, wo alles constexpr
basiert wäre, außer von enable_if
(aber dann ist es kein Merkmal), würden Sie gehen:
Hinweis: Ich habe std::declval
hier nicht benutzt, weil es einen unevaluierten Kontext benötigt (zB sizeof
oder decltype
meistens). Eine zusätzliche Anforderung (nicht sofort sichtbar) ist, dass T
standardmäßig konstruierbar ist.
Wenn Sie wirklich wollen, gibt es einen Hack:
%Vor%%Vor%Und wenn ich einen Typ brauche, keine Konstante?
Ich sehe auch kein Problem (und ja, hier verwende ich declval
). Aber ... das Übergeben von Typen hat nichts mit constexpr
zu tun; constexpr
Funktionen sind nützlich, wenn sie Werte zurückgeben, an denen Sie interessiert sind. Funktionen, die komplexe Typen zurückgeben, können natürlich verwendet werden, aber sie sind nicht constexpr
und Sie verwenden den Wert nicht vom Typ.
Und was, wenn ich Trais und Typen verketten muss?
Ausgerechnet hier leuchten Funktionen:)
%Vor%Was! Betrüger! Es ist kein Merkmal definiert!
Hum ... eigentlich, das ist der Punkt. Mit decltype
wurde eine gute Anzahl von Merkmalen gerade redundant .
TROCKEN !
Vererbung funktioniert einfach!
Nehmen Sie eine grundlegende Klassenhierarchie:
%Vor%Und definieren Sie ein Merkmal:
%Vor% Hinweis: Es ist beabsichtigt, dass das Merkmal für Derived
nicht direkt true
oder false
angibt, sondern stattdessen das Verhalten von seinem Vorgänger übernimmt. Auf diese Weise folgt die gesamte Hierarchie automatisch, wenn der Vorgänger die Haltung ändert. Da die Basisfunktionalität meistens vom Vorfahren bereitgestellt wird, ist es sinnvoll, dem Merkmal zu folgen. Noch mehr für Typeigenschaften.
Hinweis : Die Verwendung von Ellipsen ist beabsichtigt, dies ist die Überlastungsübernahme. Eine Template-Funktion wäre eine bessere Übereinstimmung als die anderen Überladungen (keine Konvertierung erforderlich), während die Ellipse immer die schlechteste Übereinstimmung ist, die garantiert, dass sie nur diejenigen aufnimmt, für die keine andere Überladung geeignet ist.
Ich nehme an, es ist unnötig zu präzisieren, wie prägnanter der letztere Ansatz ist? Nicht nur, dass Sie die template <>
Unordnung loswerden, erhalten Sie auch kostenlos Vererbung.
Kann
enable_if
so implementiert werden?
Ich denke leider nicht, aber wie ich schon sagte: das ist kein Merkmal. Und die std
-Version funktioniert gut mit constexpr
, weil sie ein bool
-Argument und keinen Typ verwendet:)
Also Warum ?
Nun, der einzige technische Grund ist, dass ein guter Teil des Codes bereits auf einer Reihe von Merkmalen beruht, die in der Vergangenheit als Typen angegeben wurden ( std::numeric_limit
), so dass die Konsistenz dies diktieren würde.
Außerdem macht es die Migration von boost::is_*
einfach so einfach!
Ich persönlich halte das für bedauerlich. Aber ich bin wahrscheinlich sehr viel eifriger dabei, den bestehenden Code zu überprüfen, den ich geschrieben habe, als die durchschnittliche Corporation.
Ein Grund ist, dass der Vorschlag type_traits älter ist als der Vorschlag constexpr.
Ein anderer ist, dass Sie bei Bedarf eigene Spezialisierungen für Ihre eigenen Typen hinzufügen können.
Ich würde sagen, der Hauptgrund ist, dass type_traits
bereits Teil von tr1
war und daher im Prinzip garantiert in mehr oder weniger derselben Form im Standard endet, also vor constexpr
. Andere mögliche Gründe sind:
remove_pointer
) definieren type
anstelle von value
, also müssen sie auf diese Weise ausgedrückt werden. Verschiedene Schnittstellen für Merkmale zu haben, die Werte und Merkmale definieren, die Typen definieren, scheint nicht notwendig zu sein templated structs
kann teilweise spezialisiert sein, während Funktionen nicht können, so dass die Implementierung einiger Merkmale einfacher werden kann Für Ihre zweite Frage: As enable_if
definiert ein type
(oder nicht, wenn es falsch übergeben wird) ist ein geschachtelter Typdef innerhalb eines struct
wirklich der Weg zu gehen
Tags und Links c++ c++11 template-meta-programming typetraits