본문으로 건너뛰기

Deducing Types

1.1 Template Type Deduction

Template Type Deduction:
The process by which the compiler determines template parameter types from function arguments.

T:
The template type parameter whose type is deduced from the argument expression.

ParamType:
The declared type of the function parameter, which determines how T is deduced.

Argument Expression (expr):
The expression passed to a function template and used by the compiler for type deduction.


1) Reference and Pointer Parameters

Reference/Pointer Deduction:
When ParamType is a reference or pointer but not a universal reference, the argument's reference part is ignored and the remaining type is matched against ParamType.

Reference Removal:
If the argument expression has a reference type, its reference qualifier is ignored when deducing T.

const Preservation:
When an argument is passed through a reference or pointer, the const qualification of the referenced or pointed-to object can be preserved as part of the deduced type.

Reference-to-const:
When ParamType already contains const, the argument's const does not need to become part of T.


2) Universal References

Universal Reference:
A deduced parameter of the form T&& that can bind to both lvalues and rvalues.

Lvalue Deduction:
When an lvalue is passed to a universal reference, T is deduced as an lvalue reference.

Rvalue Deduction:
When an rvalue is passed to a universal reference, T is deduced as a non-reference type and the parameter becomes an rvalue reference.

Lvalue/Rvalue Distinction:
Universal reference deduction is special because it distinguishes between lvalue and rvalue arguments.


3) Pass-by-Value

Pass-by-Value Deduction:
When ParamType is neither a pointer nor a reference, the parameter receives a new copied or moved value.

Reference Removal:
Reference qualifiers of the argument are ignored during by-value deduction.

Top-level const Removal:
Top-level const on the argument is ignored when deducing a by-value parameter.

Top-level volatile Removal:
Top-level volatile is also ignored during by-value deduction.

Low-level const Preservation:
const belonging to an object reached through a pointer is preserved even when the pointer itself is passed by value.


4) Array and Function Arguments

Array-to-Pointer Decay:
An array argument normally decays to a pointer to its first element when passed by value.

Array Reference Deduction:
When an array is passed by reference, its actual array type and size are preserved.

Array Size Deduction:
A reference-to-array template parameter can deduce the number of elements in an array at compile time.

Function-to-Pointer Decay:
A function argument decays to a function pointer when passed by value.

Function Reference Deduction:
When a function is passed by reference, its function type is preserved.


Template Type Deduction Rules

Reference Parameter:
Ignore the argument's reference qualifier and preserve relevant const qualification.

Universal Reference Parameter:
Treat lvalue arguments specially and preserve their lvalue-reference nature.

Value Parameter:
Remove references and top-level const or volatile.

Array/Function by Value:
Decay to pointers.

Array/Function by Reference:
Preserve the original type.


1.2 auto Type Deduction

auto:
A placeholder type specifier that lets the compiler deduce a type from an initializer.

auto Type Deduction:
Type deduction for auto follows template type deduction rules in most situations.

Initializer:
The expression from which the compiler determines the type represented by auto.


1) auto and Template Deduction

Template Deduction Relationship:
auto plays a role similar to the template parameter T, while the complete declaration containing auto plays a role similar to ParamType.

Value auto:
Plain auto generally follows pass-by-value template deduction rules.

Reference auto:
auto& follows reference deduction rules.

Universal Reference auto:
auto&& can behave as a universal reference when its type is deduced from an initializer.


2) auto and const

Top-level const Removal:
Plain auto normally removes top-level const from the initializer.

Low-level const Preservation:
const that belongs to an object referenced or pointed to by the deduced type is preserved.

const auto:
Adds top-level const to the type deduced for auto.

auto&:
Preserves reference-related type information that plain auto would discard.


3) Braced Initializers

Braced Initializer:
An initializer written using {}.

std::initializer_list:
A standard library type representing a sequence of values supplied through list initialization.

auto Braced-Initializer Rule:
When applicable, auto treats a braced initializer as a std::initializer_list.

Element Type Deduction:
All elements must permit a single element type to be deduced for the std::initializer_list.

Deduction Failure:
If a single element type cannot be deduced, the declaration is rejected.


4) auto vs. Template Type Deduction

Major Difference:
auto has special deduction behavior for braced initializers, while ordinary template type deduction does not automatically treat them as std::initializer_list.

Function Return auto:
C++14 function return types declared with auto use template type deduction rules.

Lambda Parameter auto:
C++14 generic lambda parameters declared with auto also use template type deduction rules.


auto Type Deduction Rules

Plain auto:
Usually follows pass-by-value template deduction.

auto&:
Uses reference deduction.

auto&&:
Can act as a universal reference.

Braced Initializer:
May deduce std::initializer_list.

Function Return auto:
Uses template type deduction rather than the special braced-initializer rule for variable auto.


1.3 decltype

decltype:
A type specifier that determines the type associated with a name or expression.

Exact Type Preservation:
Unlike ordinary template and auto deduction, decltype normally preserves const and reference information.

Unevaluated Expression:
The expression inspected by decltype is used to determine its type without being evaluated.


1) decltype of Names

Unparenthesized Name:
For an unparenthesized name, decltype normally reports the declared type of that name.

Declared Type:
The type explicitly or implicitly associated with the named entity.

Reference Preservation:
If the declared type is a reference, decltype preserves that reference.

const Preservation:
If the declared type contains const, decltype preserves it.


2) decltype of Expressions

Expression Type:
decltype can determine a type based on an expression and its value category.

Lvalue Expression:
For an lvalue expression of type T that is not covered by the special name rule, decltype yields T&.

Parenthesized Variable:
Parenthesizing a variable turns the operand into a general expression for decltype purposes.

decltype(x):
For an ordinary unparenthesized variable name, yields its declared type.

decltype((x)):
For an ordinary variable x, yields an lvalue reference because (x) is an lvalue expression.


3) Trailing Return Type

Trailing Return Type:
A function syntax that specifies the return type after the parameter list.

Parameter-dependent Return Type:
A trailing return type allows function parameters to be used when determining the function's return type.

decltype Return Type:
decltype can preserve the exact type produced by an expression used as a function result.


4) decltype(auto)

decltype(auto):
A placeholder type that performs type deduction using decltype rules.

Reference Preservation:
decltype(auto) can preserve references that ordinary auto deduction would remove.

const Preservation:
decltype(auto) preserves const according to decltype rules.

Return Type Deduction:
decltype(auto) is useful when a function must return exactly the type produced by an expression.


5) decltype(auto) and Parentheses

Parenthesis Sensitivity:
Parentheses can change the type deduced by decltype(auto) because they can change which decltype rule applies.

Return-by-Value:
Returning an unparenthesized local variable name may deduce the variable's declared value type.

Return-by-Reference:
Returning a parenthesized local variable may cause an lvalue-reference return type to be deduced.

Dangling Reference:
A reference that refers to an object whose lifetime has already ended.

Local Reference Hazard:
A function must not return a reference to a local object that is destroyed when the function returns.


6) Universal References and std::forward

std::forward:
Preserves the original value category of an argument when forwarding a universal reference.

Perfect Forwarding:
Passing an argument to another function while preserving important properties such as whether it was an lvalue or rvalue.

Universal Reference Return Access:
A universal-reference parameter combined with std::forward can preserve the value category of the original argument when accessing or forwarding it.


decltype Rules

decltype(name):
Returns the declared type for an unparenthesized name.

decltype(lvalue-expression):
Returns an lvalue-reference type.

decltype((name)):
Usually returns an lvalue reference for an ordinary variable.

decltype(auto):
Deduces a type using decltype rules.

Main Purpose:
Preserve exact type information when ordinary auto deduction would discard it.


1.4 Viewing Deduced Types

Type Inspection:
The process of determining what type the compiler has deduced for an expression, variable, or template parameter.


1) IDE Type Inspection

IDE Type Display:
Development environments can often display a deduced type when inspecting or hovering over an entity.

IDE Limitation:
Displayed types may become difficult to understand or may not expose type information in the most useful form for complex types.


2) Compiler Diagnostics

Compiler Diagnostic:
An error or warning message generated by the compiler.

Type Display through Errors:
A deliberately incomplete template can force the compiler to print a deduced type as part of an error message.

Type Displayer:
A debugging technique that instantiates an undefined or incomplete class template with the type being inspected.


3) typeid

typeid:
An operator that obtains runtime type information represented by std::type_info.

std::type_info:
A standard library type containing runtime type information.

std::type_info::name():
Returns an implementation-dependent textual representation of a type.

typeid Limitation:
The reported type may omit reference and cv-qualification information needed to understand the exact original type.


4) Boost.TypeIndex

Boost.TypeIndex:
A Boost library for obtaining readable type information.

type_id_with_cvr:
A Boost.TypeIndex facility that preserves const, volatile, and reference qualifiers when reporting a type.

CVR Qualifiers:
Collective term for const, volatile, and reference qualifiers.


5) Understanding Deduction Rules

Type Inspection Tools:
IDEs, compiler diagnostics, runtime type information, and libraries can help verify deduced types.

Deduction Rule Knowledge:
Type inspection tools do not replace understanding the language rules that determine the deduced type.


Notes

Template Type Deduction:
The foundation of modern C++ type deduction.

Pass-by-Value:
Removes references and top-level const or volatile.

Reference Deduction:
Preserves relevant const information of the referenced object.

Universal Reference:
Treats lvalue and rvalue arguments differently.

Array/Function Decay:
Arrays and functions decay to pointers during by-value deduction.

auto:
Usually follows template type deduction.

Braced Initializer with auto:
Has special std::initializer_list deduction behavior.

decltype:
Normally preserves the exact declared or expression-derived type.

decltype(x):
Returns the declared type of an ordinary unparenthesized variable.

decltype((x)):
Returns an lvalue-reference type for an ordinary variable.

decltype(auto):
Performs automatic deduction using decltype rules.

std::forward:
Preserves the value category of a universal-reference argument.

Type Inspection:
Use IDEs, compiler diagnostics, or Boost.TypeIndex to verify deductions, but understand the deduction rules themselves.