Was sind Vorlagenabzugsrichtlinien und wann sollten wir sie verwenden?

87

Der C ++ 17-Standard führt "Anleitungen zum Abziehen von Vorlagen" ein. Ich habe festgestellt, dass sie etwas mit dem Abzug neuer Vorlagenargumente für Konstruktoren zu tun haben, die in dieser Version des Standards eingeführt wurden, aber ich habe noch keine einfache Erklärung im FAQ-Stil gesehen, was sie sind und wofür sie sind.

  • Was sind Vorlagenabzugshandbücher in C ++ 17?

  • Warum (und wann) brauchen wir sie?

  • Wie erkläre ich sie?

Tristan Brindle
quelle
Insbesondere würde mich interessieren, ob die C ++ 17 STL tatsächlich Abzugsleitfäden bereitstellt (z. B. für std :: pair oder std :: tuple). Was ist die vollständige Liste der "ableitbaren" Standardvorlagentypen ab C ++ 17?
Quuxplusone
Es würde mich interessieren, ob ein Compiler dies unterstützt. Ich habe gcc, clang und vc ++ ausprobiert. rextester.com/DHPHC32332 Nevermind, ich fand, dass es nur mit gc ++ 8.1 C ++ 17 und 2a funktioniert. g ++ -std = c ++ 17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Jean-Simon Brochu

Antworten:

97

Vorlagenabzugsleitfäden sind Muster, die einer Vorlagenklasse zugeordnet sind und dem Compiler mitteilen, wie eine Reihe von Konstruktorargumenten (und ihre Typen) in Vorlagenparameter für die Klasse übersetzt werden sollen.

Das einfachste Beispiel ist das von std::vectorund sein Konstruktor, der ein Iteratorpaar verwendet.

template<typename Iterator>
void func(Iterator first, Iterator last)
{
  vector v(first, last);
}

Der Compiler muss , um herauszufinden , was vector<T>‚s TArt sein wird. Wir wissen, was die Antwort ist; Tsollte sein typename std::iterator_traits<Iterator>::value_type. Aber wie können wir dem Compiler mitteilen, ohne tippen zu müssen?vector<typename std::iterator_traits<Iterator>::value_type> ?

Sie verwenden einen Abzugsleitfaden:

template<typename Iterator> vector(Iterator b, Iterator e) -> 
    vector<typename std::iterator_traits<Iterator>::value_type>;

Dies teilt dem Compiler mit, dass beim Aufrufen eines vectorKonstruktors, der diesem Muster entspricht, die vectorSpezialisierung mithilfe des Codes rechts von abgeleitet wird-> .

Sie benötigen Hilfslinien, wenn der Abzug des Typs von den Argumenten nicht auf dem Typ eines dieser Argumente basiert. Beim Initialisieren von a vectorvon wird initializer_listexplizit das vector's verwendetT , sodass keine Anleitung erforderlich ist.

Die linke Seite gibt nicht unbedingt einen tatsächlichen Konstruktor an. Wenn Sie die Ableitung von Vorlagenkonstruktoren für einen Typ verwenden, stimmt dies mit den Argumenten überein, die Sie für alle Abzugsleitfäden übergeben (tatsächliche Konstruktoren der primären Vorlage bieten implizite Hilfslinien). Wenn es eine Übereinstimmung gibt, wird diese verwendet, um zu bestimmen, welche Vorlagenargumente für den Typ bereitgestellt werden sollen.

Sobald dieser Abzug erfolgt ist und der Compiler die Vorlagenparameter für den Typ ermittelt hat, erfolgt die Initialisierung für das Objekt dieses Typs so, als ob nichts davon geschehen wäre. Das heißt, der ausgewählte Abzugsleitfaden muss nicht mit dem ausgewählten Konstruktor übereinstimmen .

Dies bedeutet auch, dass Sie Hilfslinien mit Aggregaten und Aggregatinitialisierung verwenden können:

template<typename T>
struct Thingy
{
  T t;
};

Thingy(const char *) -> Thingy<std::string>;

Thingy thing{"A String"}; //thing.t is a `std::string`.

Abzugsleitfäden werden daher nur verwendet, um den zu initialisierenden Typ herauszufinden. Sobald diese Feststellung getroffen wurde, funktioniert der eigentliche Initialisierungsprozess genauso wie zuvor.

Nicol Bolas
quelle
6
Hmm, mir ist gerade eingefallen, dass selbst mit der Anleitung vector v{first, last};nicht das Richtige getan wird :(
TC
3
@TC ... es sei denn, das Richtige ist, einen Vektor von Iteratoren zu erstellen. Und std::string{32,'*'}[0] == ' '(für ASCII). Dies ist jedoch seit C ++ 11 der Fall.
Arne Vogel
2
Was passiert mit dem Allokatorvektorparameter? Was würde passieren, wenn der Allokatorvektorparameter kein Standardargument hätte? (Sie können es nicht von InputIterator ableiten)
gnzlbg
@NicolBolas: Würde es Ihnen etwas ausmachen, die Details zu erklären, wie implizite und explizite Ableitungsleitfäden im Kontext teilweise oder vollständig spezialisierter Klassen funktionieren können (deren Konstruktoren müssen eindeutig keine Parametertypen haben, die mit denen der primären Vorlage übereinstimmen)? Es ist schwierig, Informationen dazu durch eine schnelle Suche zu finden.
user541686
1
@NicolBolas: Ich verstehe. Mir ist nicht klar, dass es bei der Frage überhaupt um explizite Abzugsleitfäden geht ... Ich denke, es ist hilfreich, wenn Sie nur das einfügen, was Sie buchstäblich in diesen Kommentar geschrieben haben.
user541686