본문으로 건너뛰기

auto

2.1 Prefer auto to Explicit Type Declarations

auto:
A type specifier that lets the compiler deduce a variable's type from its initializer.

Type Inference:
The process of determining a type automatically from the surrounding program context.

Initializer Requirement:
A variable declared with auto must have an initializer so that its type can be deduced.


1) Automatic Type Deduction

Automatic Type Deduction:
Allows the compiler to determine a variable's type instead of requiring the programmer to write it explicitly.

Initialization Safety:
Because auto requires an initializer, it prevents declarations of uninitialized auto variables.

Complex Type Simplification:
auto avoids explicitly writing types that are long, complicated, or dependent on templates.

Compiler-known Type:
A type that can be determined by the compiler but may be difficult or impossible for the programmer to write directly.

Closure Type:
The unique compiler-generated type of an object created from a lambda expression.

Generic Lambda:
A C++14 lambda whose parameters can use auto, allowing their types to be deduced when the lambda is called.


2) auto and Callable Objects

Callable Object:
An object that can be invoked using function-call syntax.

std::function:
A standard library class template that can store and invoke compatible callable objects.

Function Signature:
The parameter and return-type information describing how a callable object is invoked.

Closure Storage with auto:
Using auto to store a lambda preserves the lambda's actual closure type.

Closure Storage with std::function:
Storing a lambda in std::function converts it into a general-purpose callable wrapper with a specified function signature.

Storage Overhead:
A std::function object may require more memory than storing the closure directly with auto.

Heap Allocation:
std::function may allocate dynamic memory when its internal storage is insufficient for the callable object.

Invocation Overhead:
Calling through std::function may involve indirect calls and can inhibit optimizations such as inlining.


3) Avoiding Type Mismatches

Type Mismatch:
A difference between the explicitly declared type of a variable and the actual type produced by its initializer.

Implicit Conversion:
A conversion automatically performed by the compiler when two types do not directly match.

Type Shortcut:
Using a simpler explicit type in place of the exact type produced by an expression.

Container size_type:
The implementation-appropriate unsigned integer type used to represent a container's size.

Portability Problem:
A problem caused by assumptions about types or sizes that may differ between platforms.

auto Type Matching:
Using auto ensures that the variable's type is derived directly from the initializer rather than from a manually chosen approximation.


4) Container Element Types

Associative Container Element:
An element stored by an associative container according to the container's required key and value types.

std::unordered_map Element Type:
An element of std::unordered_map<Key, T> has a key that is const, so its stored pair type contains const Key.

Unintended Temporary:
A temporary object that may be created when an explicitly declared type does not exactly match the actual element type.

Reference Binding:
A reference binds directly to an object only when the required type rules are satisfied; otherwise a conversion may create a temporary object.

const auto&:
A declaration that deduces the exact element type while accessing it through a non-modifying reference.


5) Refactoring

Refactoring:
Changing the structure or implementation of code without intentionally changing its behavior.

Type Adaptability:
An auto variable automatically adapts when the type of its initializer changes.

Explicit Type Maintenance:
Explicit declarations may need to be manually updated when the type produced by an expression changes.


6) Appropriate Use of auto

Prefer auto:
Use auto when it prevents unnecessary type repetition, avoids type mismatches, or makes code easier to maintain.

Explicit Type Declaration:
May still be preferable when writing the type directly makes the code clearer or better communicates intent.

Deduction Limitation:
auto is only as appropriate as the type produced by its initializer; some expressions produce types that are undesirable to store directly.


2.2 Explicitly Typed Initializer Idiom

Explicitly Typed Initializer Idiom:
A technique that uses auto together with an explicit conversion so that the compiler deduces the intended type.

Undesired Type Deduction:
Occurs when auto correctly deduces the initializer's actual type, but that type is not the type the programmer intends to store.


1) Proxy Classes

Proxy Class:
A class designed to represent, emulate, or augment the behavior of another type.

Proxy Object:
An object of a proxy class that stands in for another value or object.

Invisible Proxy:
A proxy type designed to behave like another type without being obvious to the caller.

Proxy Conversion:
An implicit conversion that allows a proxy object to behave like the type it represents.


2) std::vector<bool>::reference

std::vector<bool>:
A specialization of std::vector that may store boolean values in a packed bit representation.

Bit-packed Representation:
A storage representation in which individual boolean values occupy bits rather than separate bool objects.

Bit Reference Limitation:
C++ references cannot directly refer to individual bits.

std::vector<bool>::reference:
A proxy type returned by std::vector<bool>::operator[] that behaves similarly to a reference to a boolean value.

Implicit bool Conversion:
std::vector<bool>::reference can be converted to bool when an actual boolean value is required.


3) auto with Proxy Types

Proxy Type Deduction:
auto deduces the actual proxy type returned by an expression rather than the conceptual type represented by the proxy.

Proxy Lifetime:
Some proxy objects are intended to exist only briefly as part of an expression.

Temporary Object:
An unnamed object whose lifetime commonly ends at the end of the full expression that created it.

Dangling Pointer:
A pointer that refers to storage whose lifetime has already ended.

Dangling Proxy:
A proxy object whose internal reference or pointer refers to an object that no longer exists.

Undefined Behavior:
Program behavior for which the C++ language imposes no requirements.


4) Detecting Proxy Types

Library Documentation:
Documentation may identify interfaces that return proxy objects instead of ordinary values or references.

Function Signature:
The declared return type of a function can reveal that an operation returns a proxy type.

Interface Inspection:
Examining library declarations can determine the actual type returned by an expression.

Unexpected Return Type:
A return type different from the expected value or reference type can indicate the use of a proxy class.


5) Expression Templates

Expression Template:
A technique that represents an expression using temporary template-based proxy objects instead of immediately computing the final result.

Expression Proxy:
An intermediate object representing part or all of an expression.

Deferred Evaluation:
Delays computation until the final result is required.

Numeric Optimization:
Expression templates can reduce unnecessary intermediate objects and improve the efficiency of numerical expressions.


6) Forcing the Desired Type

Explicit Conversion:
A conversion explicitly requested by the programmer rather than automatically performed by the compiler.

static_cast:
A C++ cast used to request supported compile-time checked conversions between types.

Explicitly Typed Initializer:
An initializer explicitly converted to the desired type before auto performs type deduction.

auto value = static_cast<T>(expression);

auto then deduces T because the converted initializer expression has type T.


7) Converting Proxy Types

Proxy-to-Value Conversion:
Explicitly converts a proxy object into the value type it represents.

auto highPriority =
static_cast<bool>(features(w)[5]);

The proxy is converted to bool before the temporary object on which it depends is destroyed.


8) Intentional Type Conversion

Intentional Conversion:
A conversion deliberately requested because the destination type better matches the program's requirements.

Precision Reduction:
Deliberately converting a value to a type with lower numerical precision.

auto ep = static_cast<float>(calcEpsilon());

Floating-point-to-Integer Conversion:
Deliberately converting a floating-point result to an integer type.

auto index = static_cast<int>(d * c.size());

Conversion Intent:
An explicit cast documents that the difference between the initializer's original type and the variable's final type is intentional.


Explicitly Typed Initializer Rules

Normal Case:
Prefer ordinary auto when the initializer already has the desired type.

Proxy Case:
Do not blindly store an invisible proxy type when the intended result is the represented value.

Desired Different Type:
Use an explicitly typed initializer when the variable should intentionally have a type different from the initializer's original type.

General Form:

auto variable = static_cast<DesiredType>(expression);