Type Deduction - auto and decltype
auto and decltype are powerful type deduction tools introduced in C++11. They not only make code more concise but also enhance the expressive power of templates and generics.
| Book | Video | Code | X |
|---|---|---|---|
| cppreference-auto / cppreference-decltype / markdown | Video Explanation | Practice Code |
Why were they introduced?
- Solve the problem of overly complex type declarations
- Need to obtain object or expression types in template applications
- Support lambda expression definitions
What's the difference between auto and decltype?
- auto is often used for variable definitions, and the deduced type may lose const or reference (can be explicitly specified with auto &)
- decltype obtains the exact type of an expression
- auto generally cannot be used as a template type parameter
I. Basic Usage and Scenarios
Declaration and Definition
Acts as a type placeholder to assist in variable definition or declaration. When using auto, the variable must be initialized, while decltype can be used without initialization.
int b = 2;
auto b1 = b;
decltype(b) b2 = b;
decltype(b) b3; // Can be used without initialization
Expression Type Deduction
Often used for complex expression type deduction to ensure calculation precision
int a = 1;
auto b1 = a + 2;
decltype(a + 2 + 1.1) b2 = a + 2 + 1.1;
auto c1 = a + '0';
decltype(2 + 'a') c2 = 2 + 'a';
Complex Type Deduction
Iterator Type Deduction
std::vector<int> v = {1, 2, 3};
auto it = v.begin(); // Automatically deduce iterator type
// decltype(v.begin()) it = v.begin();
for (; it != v.end(); ++it) {
std::cout << *it << " ";
}
Function Type Deduction
For complex types like functions or lambda expressions, auto and decltype are commonly used. Generally, lambda definitions use auto, while template type parameters use decltype.
int add_func(int a, int b) {
return a + b;
}
int main() {
auto minus_func = [](int a, int b) { return a - b; };
std::vector<std::function<decltype(add_func)>> funcVec = {
add_func,
minus_func
};
funcVec[0](1, 2);
funcVec[1](1, 2);
//...
}
Function Return Type Deduction
Syntax Sugar Usage
auto supports trailing return type function definitions and can be used with decltype for return type deduction.
auto main() -> int {
return 0;
}
auto add(int a, double b) -> decltype(a + b) {
return a + b;
}
Function Template Return Type Deduction
When the template return type cannot be determined, auto + decltype can be used for deduction, allowing add to support general types like int, double,... and complex types like Point, Vec,... enhancing generic programming expressiveness. (In C++14, decltype can be omitted)
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
Class/Structure Member Type Deduction
struct Object {
const int a;
double b;
Object() : a(1), b(2.0) { }
};
int main() {
const Object obj;
auto a = obj.a;
std::vector<decltype(obj.b)> vec;
}
II. Important Notes - The Impact of Parentheses
Difference between decltype(obj) and decltype( (obj) )
- Generally,
decltype(obj)obtains its declared type - While
decltype( (obj) )obtains the type of the(obj)expression (lvalue expression)
int a = 1;
decltype(a) b; // Deduction result is a's declared type int
decltype( (a) ) c; // Deduction result is the type of (a) lvalue expression int &
Difference between decltype(obj.b) and decltype( (obj.b) )
decltype( (obj.b) ): Type deduction from expression perspective, obj's definition type affects deduction result. For example, if obj is const-qualified, const will limit obj.b access to const.decltype(obj.b): Since it deduces the member's declared type, it won't be affected by obj's definition.
struct Object {
const int a;
double b;
Object() : a(1), b(2.0) { }
};
int main() {
Object obj;
const Object obj1;
decltype(obj.b) // double
decltype(obj1.b) // double
decltype( (obj.b) ) // double &
decltype( (obj1.b) ) // Affected by obj1's const qualification, so it's const double &
}
Rvalue Reference Variables are Lvalues in Expressions
int &&b = 1;
decltype(b) // Deduction result is declared type int &&
decltype( (b) ) // Deduction result is int &