Wie sieht `void_t Arbeit
Sah ich Walter Browns Vortrag auf Cppcon14 über moderne template-Programmierung ( Teil I , Teil II), wo er präsentiert seine void_t
SFINAE-Technik.
Beispiel:
Eine einfache variable Vorlage ausgewertet void
wenn alle template-Argumente sind gut ausgebildet:
template< class ... > using void_t = void;
ist und die folgende Eigenschaft, die überprüft die Existenz einer member-variable namens Mitglied:
template< class , class = void >
struct has_member : std::false_type
{ };
//specialized as has_member< T , void > or discarded (sfinae)
template< class T >
struct has_member< T , void_t< decltype( T::member ) > > : std::true_type
{ };
Habe ich versucht zu verstehen, warum und wie das funktioniert. Daher ein kleines Beispiel:
class A {
public:
int member;
};
class B {
};
static_assert( has_member< A >::value , "A" );
static_assert( has_member< B >::value , "B" );
1. has_member< A >
has_member< A , void_t< decltype( A::member ) > >
A::member
existiertdecltype( A::member )
wohlgeformt istvoid_t<>
ist gültig und wird ausgewertetvoid
has_member< A , void >
und deshalb wählt es die spezielle Vorlagehas_member< T , void >
und ausgewertettrue_type
2. has_member< B >
has_member< B , void_t< decltype( B::member ) > >
B::member
existiert nichtdecltype( B::member )
ist schlecht ausgebildet und schlägt im hintergrund fehl (sfinae)has_member< B , expression-sfinae >
also diese Vorlage ist verworfen
- compiler findet
has_member< B , class = void >
mit void als Standard-argument has_member< B >
ausgewertetfalse_type
Fragen:
1. Ist mein Verständnis von diesem korrekt?
2. Walter Brown erklärt, dass die default-argument, hat genau die gleiche Art wie das void_t
für Sie zu arbeiten. Warum ist das so? (Ich sehe nicht, warum diese Typen müssen übereinstimmen, nicht einfach nur irgendein Standard-Typ macht den job?)
- Ad 2) stellen Sie die statische assert wurde geschrieben als:
has_member<A,int>::value
. Dann ist die partielle Spezialisierung, die ausgewertethas_member<A,void>
nicht mithalten können. Daher muss eshas_member<A,void>::value
oder mit syntaktischen Zucker, ein Standard-argument des Typsvoid
. - Danke, ich werde es Bearbeiten. Mh, ich sehe nicht die Notwendigkeit, mit
has_member< T , class = void >
säumigen invoid
noch. Vorausgesetzt, diese Eigenschaft wird nur verwendet werden, mit 1 template-argument zu jeder Zeit, dann wird das default-argument kann jede Art? - Interessante Frage.
- Beachten Sie, dass In diesem Vorschlag, open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf, Walter geändert
template <class, class = void>
zutemplate <class, class = void_t<>>
. So, jetzt sind wir frei, zu tun, was wir wollen, mitvoid_t
alias-template-Umsetzung 🙂
Du musst angemeldet sein, um einen Kommentar abzugeben.
Beim schreiben
has_member<A>::value
, sucht der compiler die Namenhas_member
und findet die primäre Vorlage-Klasse, das heißt, diese Deklaration:(Im OP, das ist geschrieben als eine definition ist.)
Den template-argument-list
<A>
ist im Vergleich zu der Vorlage-parameter-Liste dieses primäre Vorlage. Da das primäre template hat zwei Parameter, aber nur eine angegeben, die restlichen parameter ist standardmäßig auf die Standard-template-argument:void
. Es ist, als wenn du geschrieben hättesthas_member<A, void>::value
.Nun, die template-Parameterliste verglichen werden alle Spezialisierungen von der Vorlage
has_member
. Nur wenn keine Spezialisierung entspricht, die definition der primären Vorlage dient als fall-back. Also die partielle Spezialisierung berücksichtigt:Der compiler versucht, die template-Argumente
A, void
mit den mustern definiert die partielle Spezialisierung:T
undvoid_t<..>
eins nach dem anderen. Erste, template argument Abzug durchgeführt wird. Die partielle Spezialisierung, die oben ist dann noch ein template mit template-Parametern werden müssen "gefüllt" werden durch Argumente.Das erste Muster,
T
, kann der compiler abzuleiten, die template-parameterT
. Dies ist eine triviale Abzug, aber betrachten Sie ein Muster wieT const&
, wo wir noch ableitenT
. Für das MusterT
und das Vorlage-argumentA
wir ableitenT
zuA
.In das zweite Muster
void_t< decltype( T::member ) >
der template-parameterT
erscheint in einem Kontext, wo es abgeleitet werden kann aus jedem template-argument. Es gibt zwei Gründe für dieses:Den Ausdruck in
decltype
ist ausdrücklich ausgeschlossen von der template-argument Abzug. Ich denke, das ist, weil es können beliebig Komplex sein.Sogar, wenn wir ein Muster ohne
decltype
wievoid_t< T >
, dann der Abzug vonT
passiert, auf die gelöst alias-Vorlage. Das heißt, wir lösen die alias-Vorlage und versuchen Sie dann, Rückschlüsse auf die ArtT
aus dem resultierenden Muster. Das resultierende Muster ist jedochvoid
, die ist nicht abhängig vonT
und daher nicht erlauben, uns zu finden, einen bestimmten Typ fürT
. Dies ist ähnlich dem mathematischen problem zu versuchen, kehren Sie eine Konstante Funktion (im mathematischen Sinne dieser Begriffe).Template-argument Abzug ist fertig(*), nun ist die abgeleitet template-Argumente ersetzt werden. Dies schafft eine Spezialisierung, die wie folgt aussieht:
Art
void_t< decltype( A::member ) > >
können jetzt ausgewertet werden. Es ist gut gebildet, nach der substitution, also keine Substitution Ausfall Auftritt. Wir erhalten:Nun, wir können vergleichen, die template-Parameterliste diese Spezialisierung mit template-Argumente geliefert, um die ursprünglichen
has_member<A>::value
. Beide Arten genau übereinstimmen, so ist diese partielle Spezialisierung gewählt wird.Auf der anderen Seite, wenn wir definieren die Vorlage:
Wir am Ende mit der gleichen Spezialisierung:
aber unsere template-argument-Liste für
has_member<A>::value
jetzt ist<A, int>
. Die Argumente, die nicht den Parametern entsprechen, die von der Spezialisierung, und die primäre Vorlage gewählt wird als fall-back.(*) Der Standard, IMHO verwirrend, beinhaltet die substitution Prozess und die Abstimmung von explizit angegebenen template-Argumente in der template-argument Abzug Prozess. Zum Beispiel (post-N4296) [temp.Klasse.spec.match]/2:
Aber nicht nur bedeuten, dass alle template-Parameter der partiellen Spezialisierung werden müssen, abgeleitet; es bedeutet auch, dass die substitution muss erfolgreich sein und (wie es scheint?) die template-Argumente übereinstimmen, die (substituierten) - template-Parameter der teilweisen Spezialisierung. Beachten Sie, dass ich bin mir nicht ganz bewusst wo die Norm gibt der Vergleich zwischen den substituierten argument-Liste und den mitgelieferten Liste von Argumenten.
Dass die oben genannten Spezialisierung existiert nur, wenn es wohlgeformt ist, so dass, wenn
decltype( T::member )
gültig ist und nicht mehrdeutig.die Spezialisierung ist also für
has_member<T , void>
als Staat in den Kommentar.Beim schreiben
has_member<A>
ist eshas_member<A, void>
weil der Standard-template-argument.Und wir haben eine Spezialisierung für
has_member<A, void>
(also Erben vontrue_type
), aber wir haben keine Spezialisierung fürhas_member<B, void>
(so verwenden wir die Standard-definition : Erben vonfalse_type
)