Name already in use
cpp-docs / docs / cpp / decltype-cpp.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a function template whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a function template that wraps a call to another function, and then returns the return type of the wrapped function.
decltype( expression )
expression
An expression. For more information, see Expressions.
The type of the expression parameter.
The decltype type specifier is supported in Visual Studio 2010 or later versions, and can be used with native or managed code. decltype(auto) (C++14) is supported in Visual Studio 2015 and later.
The compiler uses the following rules to determine the type of the expression parameter.
If the expression parameter is an identifier or a class member access, decltype(expression) is the type of the entity named by expression . If there’s no such entity or the expression parameter names a set of overloaded functions, the compiler yields an error message.
If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored.
If the expression parameter is an rvalue, decltype(expression) is the type of expression . If the expression parameter is an lvalue, decltype(expression) is an lvalue reference to the type of expression .
The following code example demonstrates some uses of the decltype type specifier. First, assume that you’ve coded the following statements.
Next, examine the types that are returned by the four decltype statements in the following table.
Statement | Type | Notes |
---|---|---|
decltype(fx()); | const int&& | An rvalue reference to a const int . |
decltype(var); | int | The type of variable var . |
decltype(a->x); | double | The type of the member access. |
decltype((a->x)); | const double& | The inner parentheses cause the statement to be evaluated as an expression instead of a member access. And because a is declared as a const pointer, the type is a reference to const double . |
decltype and auto
In C++14, you can use decltype(auto) with no trailing return type to declare a function template whose return type depends on the types of its template arguments.
In C++11, you can use the decltype type specifier on a trailing return type, together with the auto keyword, to declare a function template whose return type depends on the types of its template arguments. For example, consider the following code example in which the return type of the function template depends on the types of the template arguments. In the code example, the UNKNOWN placeholder indicates that the return type can’t be specified.
The introduction of the decltype type specifier enables a developer to obtain the type of the expression that the function template returns. Use the alternative function declaration syntax that is shown later, the auto keyword, and the decltype type specifier to declare a late-specified return type. The late-specified return type is determined when the declaration is compiled, instead of when it’s coded.
The following prototype illustrates the syntax of an alternative function declaration. The const and volatile qualifiers, and the throw exception specification are optional. The function_body placeholder represents a compound statement that specifies what the function does. As a best coding practice, the expression placeholder in the decltype statement should match the expression specified by the return statement, if any, in the function_body .
auto function_name ( parameters opt ) const opt volatile opt -> decltype( expression ) noexcept opt < function_body >;
In the following code example, the late-specified return type of the myFunc function template is determined by the types of the t and u template arguments. As a best coding practice, the code example also uses rvalue references and the forward function template, which support perfect forwarding. For more information, see Rvalue reference declarator: &&.
decltype and forwarding functions (C++11)
Forwarding functions wrap calls to other functions. Consider a function template that forwards its arguments, or the results of an expression that involves those arguments, to another function. Furthermore, the forwarding function returns the result of calling the other function. In this scenario, the return type of the forwarding function should be the same as the return type of the wrapped function.
In this scenario, you can’t write an appropriate type expression without the decltype type specifier. The decltype type specifier enables generic forwarding functions because it doesn’t lose required information about whether a function returns a reference type. For a code example of a forwarding function, see the previous myFunc function template example.
The following code example declares the late-specified return type of function template Plus() . The Plus function processes its two operands with the operator+ overload. So, the interpretation of the plus operator ( + ) and the return type of the Plus function depends on the types of the function arguments.
Visual Studio 2017 and later: The compiler parses decltype arguments when the templates are declared rather than instantiated. So, if a non-dependent specialization is found in the decltype argument, it won’t be deferred to instantiation-time; it’s processed immediately and any resulting errors are diagnosed at that time.
The following example shows such a compiler error that is raised at the point of declaration:
Секреты auto и decltype
1. Какой тип будет у переменных ri1..riN после выполнения следующего кода?
Скомпилируются ли следующие фрагменты?
Теория
К механизму вывода типов, используемому в шаблонах в С++11 добавилось два новых механизма: auto и decltype. И чтобы жизнь программистам не казалась медом, все эти 3 механизма выводят типы по-своему. Механизм, используемый auto, в точности копирует механизм шаблонов, за исключением типа std::initializer_list.
Объяснений такому поведению немного и все они не отличаются внятностью. Скотт Мейерс, например, по этому поводу пишет так: “I have no idea why type deduction for auto and for templates is not identical. If you know, please tell me!”. В С++14 этот механизм менять не собираются. За объяснение можно попровать принять тот факт, что работают, например, такие удивительные вещи:
Итак, как же `auto` выводит тип? К сожалению, здесь нет простого правила на все случаи жизни, кроме, пожалуй, того, что `auto` при выводе типа в общем случае отбрасывает cv квалификаторы и ссылки. Ниже я перечислю самые важные моменты.
1.
Если тип some_expression T* или const T*, то тип var также будет T* или const T* соответственно. Пока без сюрпизов. Дальше — интереснее. Пожалуй самое важное с практической точки зрения правило заключается в том, что если тип some_expression — T, const T, T& или const T&, то типом var будет T. Это, впрочем, если задуматься, вполне логично, ведь в этом случае значение, возвращаемое some_expression копируется в var и можно смело писать вот так:
2.
В этом случае, ожидаемо, если тип some_expression — T или const T, компилироваться это не будет, так как lvalue ссылку нельзя инициализировать rvalue. Если тип some_expression — T&, то и var будет иметь тип T&. Здесь важным моментом является то, что если тип some_expression — const T&, то и тип var будет const T&.
3.
Здесь действует придуманное (или по крайней мере озвученное) Скоттом Мейерсом правило “универсальных ссылок”. Оно заключается в том, что тип var будет зависеть от того какая value category у some_expression. Если rvalue, то тип var будет T&&, если же lvalue, то T&. Cv квалификаторы при этом сохраняются.
Auto как параметр функции
auto нельзя использовать в качестве параметра функции и изменений в этом поведении не предвидется. Очевидно, тут дело в том, что если бы такое было разрешено, то, получается, любую обычную функцию можно было бы объявить по сути неявно шаблонной. И становится непонятно как разрешать перегрузку. Представьте себу такую ситуацию:
Однако в с++14 можно будет использовать auto параметры в лямбдах.
decltype
С decltype ситуация с одной стороны сложнее (если посмотреть формальные правила), с другой стороны проще (если выделить основные моменты). Я сформулирую эти правила так, как я их понял.
Итак, следует различать два основных случая применения decltype.
1. decltype(var), когда var — это объявленная переменная (например в функции или как член класса). В этом случае decltype(var) будет иметь в точности тот тип, с которым объявлена переменная.
2. decltype(expr), expr — выражение. В этом случае типом decltype(expr) будет тип, которое могло бы вернуть это выражение, с той оговоркой, что decltype(expr) будет иметь тип T& (const T&), если expr возвращает lvalue, T, если expr возвращает rvalue типа Т (const T) и T&& (const T&&), если expr возвращает xvalue (rvalue reference).
Что значит “могло бы вернуть”? Это значит то, что decltype не вычисляет переданное ему в качестве аргумента выражение.
Несколько поясняющих примеров:
В том случае, если мы не знаем lvalue нам вернет выражение, rvalue или xvalue, а тип использовать хочется, можно воспользоваться стандартным шаблоном std::remove_reference, чтобы “очистить” тип от ссылок.
Decltype(auto)
Это новая “фишка” языка, которая войдет в С++14. Она нужна для сохранения семантики decltype при объявлении auto переменных и будет использоваться в тех случаях, когда нас не будет устраивать то, что auto отбрасывает ссылки и cv квалификаторы и, возможно, в связке с новой возможностью С++14 — выводом типа возвращаемого функцией значения.
В последнем случае мы могли бы написать decltype(foo()), но представьте, если бы вместо foo() было выражение на 2 строчки, а такие в С++ не редкость.
Ответы
Ну и сейчас, загрузив теорию в кэш, можно попытаться ответить на вопросы теста.
What is decltype and how is it used?
I haven’t been able to find a good explanation of decltype. Please tell me, as a beginning programmer, what it does and why it is useful.
For example, I am reading a book that asked the following question. Can someone explain to me the answer and why, along with some good (beginner-level) examples?
What would be the type of each variable and what value would each variable have when the code finishes?
A line-by-line explanation would be very helpful.
1 Answer 1
decltype is a way to specify a type: You give it an expression, and decltype gives you back a type which corresponds to the type of the expression. Specifically, decltype(e) is the following type:
If e is the name of a variable, i.e. an "id-expression", then the resulting type is the type of the variable.
Otherwise, if e evaluates to an lvalue of type T , then the resulting type is T & , and if e evaluates to an rvalue of type T , then the resulting type is T .
Combining these rules with reference collapsing rules allows you to make sense of decltype(e) && , which is always a "suitable" reference. (C++14 also adds decltype(auto) to give you the type-deduction of auto combined with the value category semantics of decltype .)
It might be worth stressing the difference between auto and decltype : auto works on types, and decltype works on expressions.
You shouldn’t be seeing or using decltype in "day-to-day" programming. It is most useful in generic (templated) library code, where the expression in question is not known and depends on a paramater. (By contrast, auto may be used generously all over the place.) In short, if you’re new to programming, you probably won’t need to use decltype for some time.
decltype specifier
Рассматривает объявленный тип сущности или тип и категорию значения выражения.
Syntax
decltype ( entity ) | (1) | (since C++11) |
decltype ( expression ) | (2) | (since C++11) |
Explanation
Если аргумент представляет собой id-выражение без скобок, именующее структурированную привязку , то decltype дает referenced type (описано в спецификации объявления структурированной привязки).
Если аргумент представляет собой id-выражение без скобок, именующее параметр шаблона , не являющийся типом, то decltype возвращает тип параметра шаблона (после выполнения любого необходимого вывода типа, если параметр шаблона объявлен с типом-заполнителем). Тип не является константным, даже если сущность является объектом параметра шаблона (который является константным объектом).
Если выражение — это вызов функции, который возвращает значение типа класса или выражение с запятой , правым операндом которого является такой вызов функции, временный объект для этого значения не вводится.
Если выражение является значением prvalue, отличным от непосредственного вызова (возможно, заключенного в скобки) (начиная с C++20), временный объект не материализуется из этого значения prvalue: такое значение prvalue не имеет объекта результата.
Обратите внимание, что если имя объекта заключено в скобки, оно рассматривается как обычное выражение lvalue, поэтому decltype(x) и decltype((x)) часто бывают разных типов.
decltype полезен при объявлении типов, которые трудно или невозможно объявить с использованием стандартных обозначений, таких как лямбда-типы или типы, которые зависят от параметров шаблона.