ядро языка · вывод типовcore language · type deduction
Оператор, который возвращает тип выражения, не вычисляя его. Обратная задача по отношению к auto: там тип выводится из значения, здесь — берётся напрямую, с сохранением ссылок и cv-квалификаторов.An operator that yields the type of an expression without evaluating it. The inverse problem to auto: there, the type is deduced from a value; here, it's taken directly, preserving references and cv-qualifiers.
decltype(expr) — оператор, вычисляющий тип выражения expr на этапе компиляции без его выполнения (операнд — unevaluated context, как в sizeof). В отличие от auto, decltype точно сохраняет ссылки, const/volatile и разницу между lvalue и rvalue выражением.decltype(expr) is an operator that computes the type of the expression expr at compile time without evaluating it (the operand is an unevaluated context, just like in sizeof). Unlike auto, decltype exactly preserves references, const/volatile, and the distinction between an lvalue and an rvalue expression.
int x = 0;
decltype(x) a; // int — тип объявления x// int — the declared type of x
int x = 0;
decltype((x)) b = x; // int& — лишние скобки// int& — the extra parentheses
// делают x выражением-lvalue// turn x into an lvalue expression
decltype закрывает то, что auto не может решить в принципе:decltype closes a gap that auto cannot solve in principle:
До C++11 тип возврата функции, зависящий от типов параметров-шаблонов (например, результат a + b для generic T и U), нельзя было выразить — параметры ещё не объявлены в точке, где пишется обычный возвращаемый тип. auto f(T a, U b) -> decltype(a + b) решает это: тип пишется после списка параметров, когда a и b уже видны.Before C++11, a function's return type that depends on its template parameter types (say, the result of a + b for generic T and U) simply couldn't be expressed — the parameters aren't declared yet at the point where an ordinary return type would go. auto f(T a, U b) -> decltype(a + b) fixes this: the type is written after the parameter list, once a and b are already visible.
auto отбрасывает ссылки и верхнеуровневый const у инициализатора. Когда нужен ровно тот тип, что у выражения — включая & и const, — только decltype даёт такую точность.auto strips references and top-level const from the initializer. When you need exactly the type of the expression — & and const included — only decltype gives that precision.
В шаблонной библиотеке тип промежуточного результата операции над параметрами шаблона неизвестен автору заранее — decltype позволяет объявить его формально, не проговаривая явно.In a template library, the type of an intermediate result from an operation on template parameters isn't known to the author ahead of time — decltype lets it be declared formally, without spelling it out.
decltype работает по двум разным правилам в зависимости от формы операнда — это единственный оператор языка с таким раздвоением:decltype follows two different rules depending on the form of its operand — it's the only operator in the language with this split behavior:
int x = 0;
int& rx = x;
decltype(x) a; // int — операнд — просто имя переменной (id-expression):// int — the operand is just a variable name (id-expression):
// берётся тип объявления как есть// the declared type is taken as-is
decltype(rx) b = x; // int& — имя ссылки: тип объявления — int&// int& — a reference name: the declared type is int&
decltype((x)) c = x; // int& — операнд в скобках стал lvalue-выражением:// int& — the parenthesized operand became an lvalue expression:
// decltype даёт T& для lvalue, T&& для xvalue,// decltype gives T& for an lvalue, T&& for an xvalue,
// T для prvalue// T for a prvalue
decltype(x + 0) d; // int — операнд — выражение (prvalue), не имя →// int — the operand is an expression (prvalue), not a name →
// тип результата выражения// the type of the expression's result
// Trailing return type — ради чего decltype ввели в первую очередь:// Trailing return type — the primary reason decltype was introduced:
template <class T, class U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
Компилятор не генерирует для decltype никакого кода: как и sizeof, это чисто статический анализ AST выражения — операнд не выполняется, побочные эффекты (вызовы функций, инкременты) внутри него не происходят.The compiler generates no code for decltype at all: like sizeof, it's purely a static analysis of the expression's AST — the operand is never executed, and side effects inside it (function calls, increments) never happen.
decltype(x) и decltype((x)) — разные типы (int против int&) для одной и той же переменной x. Это единственное место в языке, где синтаксически «бесполезные» скобки меняют семантику — частый источник трудноуловимых багов при копипасте.decltype(x) and decltype((x)) are different types (int versus int&) for the very same variable x. This is the one place in the language where syntactically "useless" parentheses change the semantics — a frequent source of hard-to-spot bugs from copy-pasting code.
Комбинация decltype(auto), которая выводит тип переменной по правилам decltype вместо правил auto, добавлена в C++14. В C++11 её нет — только раздельные auto и decltype(expr) в trailing return.The combination decltype(auto), which deduces a variable's type by decltype's rules instead of auto's, was added in C++14. It doesn't exist in C++11 — only the separate auto and decltype(expr) in a trailing return type.
decltype(f()) не вызывает f() в рантайме — только анализирует тип возврата на этапе компиляции. Если по ошибке ожидать, что выражение внутри decltype выполнится (как в обычном коде), результат удивит отсутствием эффекта.decltype(f()) doesn't call f() at runtime — it only analyzes the return type at compile time. Mistakenly expecting the expression inside decltype to actually run, as it would in ordinary code, leads to a surprising lack of any effect.