Beim Versuch, Ist es möglich zu sagen, ob eine Klasse eine Basisfunktion in C ++ versteckt hat? , habe ich folgendes generiert:
%Vor%Was gibt was ich will:
%Vor% klingeln und gcc kompilieren und ausführen dies "richtig", aber vc ++ nicht (obwohl mir bewusst ist, dass es Probleme damit gibt, ordnungsgemäß mit ähnlichen Ausdrücken wie template <typename T> ... decltype(fn(std::declval<T>().mfn()))
zu funktionieren ).
Also meine Frage ist, wird das als gültig angesehen oder wird es später brechen? Ich bin auch neugierig darauf, dass x_hidden
als Template-Parameter in den Funktionen verwendet werden kann, aber nicht in using t = std::integral_constant<bool, x_hidden>
verwendet werden kann. Ist das nur, weil der Typ der Vorlage zu diesem Zeitpunkt noch nicht vollständig deklariert ist? Wenn ja, warum funktioniert es dann für die Funktionsdeklarationen?
Wenn x_hidden
falsch ist, gibt es keine Template-Argumente für die diese Template-Funktion
kann instanziiert werden, so dass Ihr Programm fehlerhaft ist und keine Diagnose erforderlich ist. Dies ist ein üblicher Hack, dessen Rechtswidrigkeit irgendwann klargestellt oder sogar legal sein kann.
Es gibt möglicherweise einen Grund dafür, has_x_f
zu verwenden, anstatt is_hidden
direkt mit der is_same
-Klausel zu initialisieren, aber dies wird in Ihrem Code nicht gezeigt.
Für jede Template-Spezialisierung müssen Argumente sein, die die Instanziierung gültig machen. Wenn dies nicht der Fall ist, ist das Programm schlecht ausgebildet, keine Diagnose erforderlich.
Ich glaube, dass diese Klausel im Standard enthalten ist, damit Compiler erweiterte Prüfungen von Vorlagen durchführen können, sie aber nicht benötigen.
%Vor% Der Compiler ist frei zu bemerken x_hidden
ist false
, und sagen "es ist egal, was is_same<T,R>
ist", und folgern, dass keine Template-Argumente diese Spezialisierung gültig machen könnten . Dann erzeuge einen Fehler.
Ein einfacher Hack ist
%Vor% Hier schleichen wir ein anderes Template-Argument ein, das normalerweise gleich T
ist. Nun muss der Compiler die Möglichkeit einräumen, dass T2
den Test has_x
besteht und dass das übergebene Argument R
ist. Benutzer können dies umgehen, indem sie das "falsche" T2
manuell übergeben.
Dies löst möglicherweise nicht alles. Der Standard ist ein bisschen schwierig zu lesen, aber eine Lesung besagt, dass wenn wir innerhalb des Körpers von y()
gehen und davon ausgehen, dass unser T
selbst x()
hat, verletzen wir immer noch die Regel der Möglichkeit einer gültigen Vorlage Instanziierung.
[temp.res] 14.6 / 8 (root und 1)
Wenn Sie wissen, welche Namen Typnamen sind, kann die Syntax jeder Vorlage überprüft werden. Das Programm ist schlecht geformt, keine Diagnose erforderlich, wenn:
- Für eine Vorlage [...] kann keine gültige Spezialisierung generiert werden und die Vorlage wird nicht instanziiert oder
Keine gültige Spezialisierung für
%Vor% kann generiert werden, wenn x_hidden
falsch ist. Die Notwendigkeit einer weiteren Überlastung ist unerheblich.
Wenn Sie es mit dem T2
Trick beheben, enthält dieselbe Regel , wenn der Körper T=T2
annimmt.
Drei sind Wörter im Standard, die versuchen, die Vorlage nicht in bestimmten Kontexten zu instanziieren, aber ich bin unsicher, ob dies den obigen Code gut geformt macht oder nicht.
Ich habe versucht, Ihren Code mit dem Intel C ++ Compiler (icpc (ICC) 17.0.2 20170213) zu kompilieren, und es würde nicht mit der folgenden Nachricht kompilieren:
%Vor%Ich konnte jedoch Folgendes mit dem Intel Compiler und GCC zusammenstellen.
%Vor%Ich bin mir nicht bewusst, ob das nach dem Standard falsch ist oder nicht, aber wie ich es sehe, stoßen Sie nicht auf das in einer der anderen Antworten erwähnte Problem, dass Sie eine Vorlage haben, die nicht instanziiert werden kann / p>
EDIT: Ich konnte auf MSVC 2017 durch einige Metaprogrammierungstricks für "alte Zeiten" Vorlagen kompilieren und Klassen statt Funktionen verwenden.
Wenn ich diese Implementierung von has_x
verwende, kompiliert sie stattdessen:
Vollständiger Code auf Wandbox hier .
Ich hatte ein bisschen Code Clean-up (löschte die Out-of-Line- x_hidden
-Deklaration) und endete mit dem folgenden. Ich habe es auch anhand der obigen Antwort von @ Yakk leicht korrigiert, um [temp.res] / 8 zu vermeiden es ungültig machen.
Live-Demo auf der Wandbox - gcc und clang sind beide damit zufrieden.
MSVC 2017 beschwerte sich
Fehler C2064: Begriff bewertet nicht zu einer Funktion, die 0 Argumente nimmt für beide Verwendungen von
A<T2>::x_hidden()
, wennA<B>
fürB
übernommen wird, von dem er erbt.
MSVC 2015 gab die gleiche Beschwerde und erlitt einen internen Compilerfehler. ^ _ ^
Also ich denke, das ist gültig, aber übt MSVCs constexpr
oder Template-Instantiierungs-Maschinerie auf unangenehme Weise aus.
Nach dem Beispiel in [expr.unary.op] / 3 , der Typ von &B::x
ist void (B::*)()
und der Typ von &C::x
ist void (A<C>::*)()
. Der erste has_x_f()
ist also vorhanden, wenn T
ist B
und der zweite has_x_f()
wird angezeigt, wenn T
ist C
und Base
ist A<C>
.
Nach [temp.inst] / 2 wird die Instanziierung der Deklarationen instanziiert aber nicht Definitionen der Mitglieder. Pro [temp.inst] / 3 und 4 , Mitgliederfunktionsdefinitionen (einschließlich Vorlage) Funktionen) werden nicht instanziiert, bis sie benötigt werden.
Unsere Deklarationen sind aktuell anders, da die Verwendung von R
und T2
bedeutet, dass der Compiler nicht die Wahrheit oder Unwahrheit der beiden Größen von &&
ermitteln kann.
Die Verwendung der verschiedenen Parametertypen hilft MSVC, die sie sonst als Neudefinitionen derselben Vorlagenelementvorlagenfunktion sehen würden. Meine Lektüre von [temp.inst] / 2 sagt, dass dies nicht erforderlich ist, da Sie sind nur Neudefinitionen, wenn wir sie instanziieren, und sie können nicht gleichzeitig instanziiert werden. Da wir A<T2>::x_hidden()
und !A<R>::x_hidden()
verwenden, kann der Compiler zu diesem Zeitpunkt nicht wissen, dass sie sich gegenseitig ausschließen. Ich glaube nicht, dass es notwendig ist, das zu tun, um [temp.res] / 8 einfach zu vermeiden Die Verwendung von A<R>::x_hidden()
scheint mir sicher genug zu sein. Dies sollte auch sicherstellen, dass in den beiden Templates R
tatsächlich verwendet wird.
Von da an ist es ziemlich einfach. y()
zeigt, dass wir die richtigen Werte von beiden Pfaden haben.
Abhängig von Ihrem Anwendungsfall können Sie if constexpr
mit x_hidden()
verwenden, um alle Vorlagenzauber in y_()
, pro y2()
oben zu vermeiden.
Dies vermeidet das Problem mit [temp.res] / 8 , das in @ Yakks Antwort beschrieben wird. Als problematische Klausel [temp.res] /8.1 gilt, dass die Vorlage schlecht formatiert ist, wenn
Für eine Vorlage oder eine Unteranweisung einer constexpr if-Anweisung innerhalb einer Vorlage kann keine gültige Spezialisierung generiert werden und die Vorlage wird nicht instanziert, [...]
Solange Sie A<T>::y2()
für einige T
instanziieren, unterliegen Sie nicht dieser Klausel.
Der Ansatz y2()
hat den Vorteil, mit MSVC2017 zu arbeiten, solange Sie das Compiler-Flag "/ std: c ++ latest" übergeben.
Tags und Links c++ c++11 language-lawyer c++14