zurück zum Artikel

Template Arguments

Rainer Grimm

Es ist recht interessant, welche Regeln der Compiler anwendet, um die Template-Argumente abzuleiten. Um es kurz zu machen: Fast immer resultiert der erwartete Datentyp.

Es ist recht interessant, welche Regeln der Compiler anwendet, um die Template-Argumente abzuleiten. Um es kurz zu machen: Fast immer resultiert der erwartete Datentyp.

Die Regeln gelten nicht nur fĂŒr Funktions-Templates (C++98), sondern auch fĂŒr auto (C++11), fĂŒr Klassen-Templates (C++17) und Concepts (C++20).

Template Arguments

C++ unterstĂŒtzt die Ableitung von Funktions-Template-Argumenten seit seinen AnfĂ€ngen. Hier ist eine kurze Rekapitulation.

ZunĂ€chst rufe ich ein Funktions-Template max fĂŒr int und double auf

template <typename T>
T max(T lhs, T rhs) {
return (lhs > rhs)? lhs : rhs;
}

int main() {

max(10, 5); // (1)
max(10.5, 5.5); // (2)

}

In diesem Fall leitet der Compiler die Template-Argumente aus den Funktionsargumenten ab. C++ Insights zeigt, dass der Compiler ein vollstĂ€ndig spezialisiertes Funktions-Template fĂŒr max fĂŒr int (1) und fĂŒr double (2) erstellt.

Template Arguments

Der Prozess der Template-Typ-Deduktion, wie in diesem Fall, produziert meist den erwarteten Typ. Es ist recht aufschlussreich, diesen Prozess tiefer zu analysieren.

Bei der Ableitung des Template-Typs kommen drei EntitÀten ins Spiel: T, ParameterType und der Ausdruck expression.

template <typename T>
void func(ParameterType param);

func(expression);

Es werden zwei Typen abgeleitet:

FĂŒr ParameterType gibt es die drei Möglichkeiten:

Der Ausdruck wiederum kann ein lvalue oder ein rvalue sein. ZusÀtzlich kann der lvalue oder rvalue eine Referenz, oder const oder volatile qualifiziert sein.

Der einfachste Weg, die Template-Typ-Deduktion zu verstehen, ist, den ParameterType zu variieren.

Den Parameter als Wert zu nehmen, ist wohl die am hÀufigsten verwendete Variante.

template <typename T>
void func(T param);

func(expr);

Wenn der ParameterType eine Referenz oder eine universelle Referenz ist, wird die constness (oder volatileness) von expr beachtet.

Der Einfachheit halber verwende ich eine Referenz. Die analoge Argumentation gilt fĂŒr einen Zeiger. Im Wesentlichen ergibt sich das erwartete Ergebnis.

template <typename T>
void func(T& param);
// void func(T* param);

func(expr);
template <typename T>
void func(T&& param);

func(expr);

Zugegeben, diese ErklÀrung war ziemlich technisch. Hier ist ein Beispiel.

// templateTypeDeduction.cpp

template <typename T>
void funcValue(T param) { }

template <typename T>
void funcReference(T& param) { }

template <typename T>
void funcUniversalReference(T&& param) { }

class RVal{};

int main() {

const int lVal{};
const int& ref = lVal;

funcValue(lVal); // (1)
funcValue(ref);

funcReference(lVal); // (2)

funcUniversalReference(lVal); // (3)
funcUniversalReference(RVal());

}

Ich definiere und verwende ein Funktions-Template, das sein Argument per Wert (1), per Referenz (2) und per universeller Referenz (3) nimmt.

Dank C++ Insights [1] kann ich die Typableitung des Compilers visualisieren.

Template Arguments
Template Arguments
Template Arguments

Es gibt ein interessantes Verhalten, wenn man die Funktion funcValue mit einem C-Array aufruft. Das C-Array decays (verfÀllt).

Ein C-Array als Wert anzunehmen ist besonders.

// typeDeductionArray.cpp

template <typename T>
void funcValue(T param) { }

int main() {

int intArray[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

funcValue(intArray);

}

Wird das Funktions-Template funcValue mit einem C-Array aufgerufen, decayed das C-Array in einen Zeiger auf sein erstes Element. Decay hat viele Facetten. Es wird angewendet, wenn ein Funktionsargument als Wert ĂŒbergeben wird. Decay bedeutet, dass eine implizite Konvertierung Funktion-zu-Zeiger, Array-zu-Zeiger oder lvalue-zu-rvalue gegebenenfalls angewendet wird. ZusĂ€tzlich werden die Referenz eines Typs T und seine const/volatile Qualifizierer entfernt.

Hier ist der Screenshot des Programms aus C++ Insights [2].

Template Arguments

Das bedeutet im Wesentlichen, dass die GrĂ¶ĂŸe des C-Arrays nicht bekannt ist.

Aber es gibt einen Trick. Wenn man das C-Array per Referenz nimmt und den Typ und die GrĂ¶ĂŸe des C-Arrays annimmt, ermittelt der Compiler seine GrĂ¶ĂŸe.

// typeDeductionArraySize.cpp

#include <cstddef>
#include <iostream>

template <typename T, std::size_t N>
std::size_t funcArraySize(T (&arr)[N]) {
return N;
}

int main() {

std::cout << '\n';

int intArray[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

funcArraySize(intArray);

std::cout << "funcArraySize(intArray): "
<< funcArraySize(intArray) << '\n';

std::cout << '\n';

}

Das Funktions-Template funcArraySize leitet die GrĂ¶ĂŸe des C-Arrays ab. Ich habe aus GrĂŒnden der Lesbarkeit dem C-Array Parameter den Namen arr gegeben: std::size_t funcArraySize(T (&arr)[N]). Dies ist nicht notwendig und du kannst einfach std::size_t funcArraySize(T (&)[N]) schreiben. Hier sind die Interna aus C++ Insights [3].

Template Arguments

Zum Abschluss noch die Ausgabe des Programms:

Template Arguments

Dieses Wissen ĂŒber die automatische Bestimmung der Typen der Template-Argumente, lĂ€sst sich direkt auf auto (C++11) anwenden.

Die auto Typ-Deduktion verwendet die Regeln der Template-Typ-Deduktion

Zur Erinnerung, dies sind die wesentlichen EntitÀten der Template-Typ-Deduktion:

template <typname T> 
void func(ParameterType param);

auto val = 2011;

Das VerstĂ€ndnis von auto bedeutet, dass auto als Ersatz fĂŒr T und die Typspezifizierer von auto als Ersatz fĂŒr den ParameterType in dem Funktions-Template zu betrachten ist.

Der Typspezifizierer kann ein Wert (1), eine Referenz (2) oder eine universelle Referenz (3) sein.

auto val = arg; // (1)

auto& val = arg; // (2)

auto&& val = arg; // (3)

Probieren wir es aus und Àndern das vorherige Programm templateTypeDeduction.cpp und verwenden auto anstelle von Funktions-Templates.

// autoTypeDeduction.cpp

class RVal{};

int main() {

const int lVal{};
const int& ref = lVal;

auto val1 = lVal; // (1)
auto val2 = ref;

auto& val3 = lVal; // (2)

auto&& val4 = lVal; // (3)
auto&& val5 = RVal();

}

Beim Betrachten der resultierenden Typen in C++ Insights [4] fÀllt auf, dass sie identisch mit den Typen sind, die im Programm templateTypeDeduction.cpp abgeleitet wurden.

Template Arguments

NatĂŒrlich decayed auto auch, wenn es ein C-Array als Wert annimmt.

Ich habe das pdf-Bundle vorbereitet. Es zu erhalten ist ganz einfach. Ich verschicke automatisch bei der Anmeldung an meinen deutschen oder englischen Newsletter einen Link zu dem pdf-Bundle.

Hier gibt es mehr Informationen zu dem pdf-Bundle: C++ Coroutines [5].

Template Arguments

C++17 macht Typ-Deduktion mĂ€chtiger. Erstens ist eine automatische Typableitung fĂŒr Nicht-Typ-Template-Parameter möglich und zweitens können Klassen-Templates auch ihre Argumente ableiten. Insbesondere die Klassen-Template-Argument-Deduktion macht das Programmiererleben viel einfacher. ( [6])


URL dieses Artikels:
https://www.heise.de/-6069388

Links in diesem Artikel:
[1] https://cppinsights.io/s/6bb71783
[2] https://cppinsights.io/s/910a53e4
[3] https://cppinsights.io/s/6e908572
[4] https://cppinsights.io/s/2c652b47
[5] https://www.modernescpp.com/index.php/the-new-pdf-bundle-is-ready-coroutines
[6] mailto:rainer@grimm-jaud.de