Wenn Sie has_member<A>::value
schreiben, der Compiler nachschlägt der Name has_member
und findet die primären Klassenvorlage, das heißt, diese Erklärung: (. im OP, ist, dass als Definition geschrieben)
template< class , class = void >
struct has_member;
Die Vorlagenargumentliste <A>
wird mit der Vorlagenparameterliste dieser primären Vorlage verglichen. Da die primäre Vorlage über zwei Parameter verfügt, von denen Sie jedoch nur einen Parameter angegeben haben, wird als Standardwert für den verbleibenden Parameter das Standardschablonenargument verwendet: void
. Es ist, als ob du has_member<A, void>::value
geschrieben hättest.
Jetzt wird die Vorlagenparameterliste mit allen Spezialisierungen der Vorlage has_member
verglichen. Nur wenn keine Spezialisierung übereinstimmt, wird die Definition der primären Vorlage als Fallback verwendet. So ist die partielle Spezialisierung berücksichtigt wird:
template< class T >
struct has_member< T , void_t< decltype(T::member) > > : true_type
{ };
Der Compiler versucht, die Vorlage Argumente A, void
mit den Mustern in der Teil Spezialisierung definiert anzupassen: T
und void_t<..>
eins nach dem anderen. Zuerst wird eine Vorlagenargumentableitung durchgeführt. Die obige Teilspezialisierung ist immer noch eine Vorlage mit Template-Parametern, die durch Argumente "gefüllt" werden müssen. Das erste Muster, T
, ermöglicht dem Compiler, den Template-Parameter T
abzuleiten. Dies ist eine triviale Ableitung, aber betrachten Sie ein Muster wie T const&
, wo wir noch T
ableiten könnten. Für das Muster T
und das Vorlagenargument A
folgern wir T
zu A
.
Im zweiten Muster void_t< decltype(T::member) >
erscheint der Vorlagenparameter T
in einem Kontext, in dem er nicht aus einem Vorlagenargument abgeleitet werden kann. Es gibt zwei Gründe dafür:
Der Ausdruck in decltype
ausdrücklich von Template-Argument Abzug ausgeschlossen. Ich denke, das liegt daran, dass es beliebig komplex sein kann.
Selbst wenn wir ein Muster ohne decltype
wie void_t<T>
verwenden, dann wird der Abzug von T
geschieht auf der Alias-Vorlage aufgelöst. Das heißt, wir lösen die Aliasvorlage auf und versuchen dann, aus dem resultierenden Muster den Typ T
abzuleiten.Das resultierende Muster ist jedoch void
, das nicht von T
abhängig ist und es daher nicht erlaubt, einen spezifischen Typ für T
zu finden. Dies ist ähnlich dem mathematischen Problem, eine konstante Funktion zu invertieren (im mathematischen Sinne dieser Begriffe).
Vorlage Argument Abzug ist abgeschlossen (*), jetzt die abgeleitet Vorlage Argumente ersetzt werden. Dadurch entsteht eine Spezialisierung, die wie folgt aussieht:
template<>
struct has_member< A, void_t< decltype(A::member) > > : true_type
{ };
Der Typ void_t< decltype(A::member) > >
jetzt ausgewertet werden können. Es ist nach der Substitution gut gebildet, daher tritt kein Substitutionsfehler auf. Wir erhalten:
template<>
struct has_member<A, void> : true_type
{ };
Jetzt können wir die Vorlage Parameterliste dieser Spezialisierung mit den Schablonen Argumente auf den ursprünglichen has_member<A>::value
geliefert vergleichen. Beide Typen stimmen genau überein, daher wird diese Teilspezialisierung gewählt.
Auf der anderen Seite, wenn wir die Vorlage definieren als:
template< class , class = int > // <-- int here instead of void
struct has_member : false_type
{ };
template< class T >
struct has_member< T , void_t< decltype(T::member) > > : true_type
{ };
Wir sind mit der gleichen Spezialisierung am Ende:
template<>
struct has_member<A, void> : true_type
{ };
aber unsere Template-Argument-Liste für has_member<A>::value
ist jetzt <A, int>
. Die Argumente stimmen nicht mit den Parametern der Spezialisierung überein, und die primäre Vorlage wird als Fallback ausgewählt.
(*) The Standard, verwechslungs IMHO umfasst den Substitutionsprozess und die Anpassung des explizit angegebene Vorlage Arguments in dem Argumente Vorlage Abzug Prozess. Zum Beispiel (post-N4296) [temp.class.spec.match]/2:
Eine partielle Spezialisierung entspricht eine gegebene tatsächliche Vorlage Argumentliste , wenn die Vorlage Argumente der partiellen Spezialisierung können aus der abgeleitet werden tatsächliche Vorlagenargumentliste
Aber dies bedeutet nicht nur bedeuten, dass alle Template-Parameter der partiellen Spezialisierung werden abgeleitet haben; es bedeutet auch, dass die Substitution erfolgreich sein muss und (wie es scheint?) die Template-Argumente mit den (substituierten) Template-Parametern der Teilspezialisierung übereinstimmen müssen. Beachten Sie, dass mir nicht vollständig bekannt ist, wobei der Standard den Vergleich zwischen der Liste der ersetzten Argumente und der Liste der übergebenen Argumente angibt.
Anzeige 2) Stellen Sie sich vor, die statische Assert wurde geschrieben als: 'has_member :: value'. Dann kann die Teilspezialisierung, die 'has_member 'ergibt, nicht übereinstimmen. Daher muss es 'has_member :: value' sein, oder, mit syntaktischem Zucker, ein Standardargument vom Typ' void'. – dyp
@dyp Danke, ich werde das bearbeiten. Mh, ich sehe keine Notwendigkeit, 'has_member' standardmäßig in 'void' zu haben. Angenommen, dieses Merkmal wird zu einem beliebigen Zeitpunkt nur mit einem Vorlagenargument verwendet, dann könnte das Standardargument ein beliebiger Typ sein? –
nonsensation
Interessante Frage. – cybermonkey