1. Grundlegende Einführung
decltype
ist ein neues Schlüsselwort in C++11, um den Typ eines Ausdrucks abzuleiten. Gleiche Funktion wie auto
, wird zur automatischen Typableitung zur Kompilierungszeit verwendet. Eingeführt , decltype
weil dies nicht auf alle automatischen Typableitungsszenarien anwendbar ist und die Verwendung auto
in einigen Sonderfällen auto
unpraktisch oder sogar überhaupt nicht möglich ist . decltype
Sie können sich den Operator auch einfach als eine andere Form vorstellen sizeof
, da keiner von beiden sein Argument tatsächlich auswertet, sondern als Tool zur Kompilierungszeit fungiert.
auto varName = value;
decltype(exp) varName = value;
auto
=
Der Typ der Variablen wird anhand des Anfangswerts auf der rechten Seite abgeleitet , und der Typ der Variablen wirddecltype
anhand des Ausdrucks abgeleitet, der nichts mit dem Wert auf der rechten Seite zu tun hat .exp
=
value
auto
Es ist erforderlich, dass die Variable initialisiert wird, daauto
der Variablentyp anhand des Anfangswerts der Variablen abgeleitet wird. Wenn sie nicht initialisiert ist, kann der Variablentyp nicht abgeleitet werden.- Es
decltype
ist nicht erforderlich und kann daher wie folgt geschrieben werden:
decltype(exp) varName;
Im Prinzip exp
handelt es sich nur um einen gewöhnlichen Ausdruck, der in beliebiger komplexer Form vorliegen kann, es muss jedoch gewährleistet sein, exp
dass das Ergebnis von typisiert ist und nicht sein kann void
; if ist eine Funktion , die exp
einen Wert von zurückgibt , und das Ergebnis von ist ebenfalls eingegeben wird, führt dies zu Kompilierungsfehlern.void
exp
void
2. Verschiedene Formen von Decltype
int x = 0;
decltype(x) y = 1; // y -> int
decltype(x + y) z = 0; // z -> int
const int& i = x;
decltype(i) j = y; // j -> const int&
const decltype(z) *p = &z; // *p -> const int, p -> const int*
decltype(z) *m = &z; // *m -> int, m -> int*
decltype(m)* n = &m; // *n -> int*, n -> int**
3. Ableitungsregeln
decltype
Die Ableitungsregeln von lassen sich kurz wie folgt zusammenfassen:
- Wenn
exp
es sich um einen Ausdruck handelt, der nicht()
in Klammern steht, um einen Zugriffsausdruck für Klassenmitglieder oder um eine einzelne Variable,decltype(exp)
ist der Typexp
konsistent mit .
Codebeispiel:
#include<string>
#include<iostream>
using namespace std;
class A {
public:
static int total;
string name;
int age;
float scores;
}
int A::total = 0;
int main()
{
int n = 0;
const int &r = n;
A a;
decltype(n) x = n; // n 为 int,x 被推导为 int
decltype(r) y = n; // r 为 const int &,y 被推导为 const int &
decltype(A::total) z = 0; // total 是类 A 的一个 int 类型的成员变量,z 被推导为 int
decltype(A.name) url = "www.baidu.com"; // url 为 string 类型
return 0;
}
- Wenn
exp
es sich um einen Funktionsaufruf handelt,decltype(exp)
stimmt der Typ mit dem Typ des Funktionsrückgabewerts überein.
Codebeispiel:
int& func1(int, char); // 函数返回值为 int&
int&& func2(void); // 函数返回值为 int&&
int func3(double); // 函数返回值为 int
const int& func4(int, int, int); // 函数返回值为 const int&
const int&& func5(void); // 函数返回值为 const int&&
int n = 50;
decltype(func1(100,'A')) a = n; // a 的类型为 int&
decltype(func2()) b = 0; // b 的类型为 int&&
decltype(func3(10.5)) c = 0; // c 的类型为 int
decltype(func4(1,2,3)) x = n; // x 的类型为 const int&
decltype(func5()) y = 0; // y 的类型为 const int&&
exp
Beim Aufruf einer Funktion sind Klammern und Parameter erforderlich, dies ist jedoch nur eine Formalität und führt den Funktionscode nicht tatsächlich aus.
- Wenn
exp
es sich um einen L-Wert handelt oder er()
in Klammern steht,decltype(exp)
ist der Typ vonexp
ein Verweis auf . Unter der Annahme, dassexp
der Typ von istT
, danndecltype(exp)
ist der Typ vonT&
.
Codebeispiel:
class A
{
public:
int x;
}
int main()
{
const A obj;
decltype(obj.x) a = 0; // a 的类型为 int
decltype((obj.x)) b = a; // b 的类型为 int&
int n = 0, m = 0;
decltype(m + n) c = 0; // n + m 得到一个右值,c 的类型为 int
decltype(n = n + m) d = c; // n = n + m 得到一个左值,d 的类型为 int &
return 0;
}
L-Wert: die Daten, die nach der Ausführung des Ausdrucks noch vorhanden sind, also persistente Daten; r-Wert bezieht sich auf die Daten, die nach der Ausführung des Ausdrucks nicht mehr vorhanden sind, also temporäre Daten. Eine einfache Möglichkeit zur Unterscheidung besteht darin, die Adresse des Ausdrucks zu verwenden. Wenn der Compiler keinen Fehler meldet, handelt es sich um einen L-Wert, andernfalls um einen R-Wert.
- Die statischen Mitglieder der Klasse können verwendet werden
auto
, die nicht statischen Mitglieder der Klasse können jedoch nicht verwendet werdenauto
. Wenn Sie den Typ der nicht statischen Mitglieder der Klasse ableiten möchten, können Sie ihn nur verwendendecltype
.
Codebeispiel:
template<typename T>
class A
{
private :
decltype(T.begin()) m_it;
// typename T::iterator m_it; // 这种用法会出错
public:
void func(T& container)
{
m_it = container.begin();
}
};
int main()
{
const vector<int> v;
A<const vector<int>> obj;
obj.func(v);
return 0;
}
4. Sonstiges
Darüber hinaus kann es in Funktionsvorlagen, Klassenvorlagen und lambda
Ausdrücken verwendet werden decltype
, um Typen abzuleiten oder Typen zu deklarieren.
Der unten angegebene Beispielcode zeigt die lambda
Verwendung in Ausdrücken decltype
:
#include <iostream>
int main()
{
int x = 42;
auto f = [&](decltype(x)& val) {
val += 1; };
f(x);
std::cout << "x: " << x << std::endl; // x: 43
return 0;
}
In diesem Beispiel wird ein lambda
Ausdruck definiert, f
dessen Argument decltype
den abgeleiteten Argumenttyp als verwendet int&
. Da lambda
die im Ausdruck verwendete Variable sichtbar sein muss, lambda
wird sie in der Erfassungsliste vor dem Ausdruck verwendet [&]
, damit lambda
der Ausdruck erfasst x
. lambda
Schließlich wird der Ausdruck aufgerufen f
und x
die Variable als Argument übergeben, wodurch x
der Wert von um eins erhöht wird.