The subject was discussed before, but this is not a duplicate.
When someone asks about the difference between decltype(a)
and decltype((a))
, the usual answer is - a
is a variable, (a)
is an expression. I find this answer unsatisfying.
First, a
is an expression as well. The options for a primary expression include, among others -
More importantly, the phrasing for decltype considers parentheses very, very explicitly:
For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1) if e is an unparenthesized id-expression naming a structured binding, ...
(1.2) otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3) otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4) otherwise, ...
So the question remains. Why are parentheses treated differently? Is anyone familiar with technical papers or committee discussions behind it? The explicit consideration for parentheses leads to think this is not an oversight, so there must be a technical reason I'm missing.
In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.
The following expressions do not evaluate their operands: sizeof() , typeid() , noexcept() , decltype() , and declval() .
auto is a keyword in C++11 and later that is used for automatic type deduction. The decltype type specifier yields the type of a specified expression. Unlike auto that deduces types based on values being assigned to the variable, decltype deduces the type from an expression passed to it.
Decltype keyword in C++ Decltype stands for declared type of an entity or the type of an expression. It lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression. SYNTAX : decltype( expression )
It's not an oversight. It's interesting, that in Decltype and auto (revision 4) (N1705=04-0145) there is a statement:
The decltype rules now explicitly state that
decltype((e)) == decltype(e)
(as suggested by EWG).
But in Decltype (revision 6): proposed wording (N2115=06-018) one of the changes is
Parenthesized-expression inside decltype is not considered to be an
id-expression
.
There is no rationale in the wording, but I suppose this is kind of extension of decltype using a bit different syntax, in other words, it was intended to differentiate these cases.
The usage for that is shown in C++draft9.2.8.4:
const int&& foo(); int i; struct A { double x; }; const A* a = new A(); decltype(foo()) x1 = 17; // type is const int&& decltype(i) x2; // type is int decltype(a->x) x3; // type is double decltype((a->x)) x4 = x3; // type is const double&
What is really interesting, is how it works with the return
statement:
decltype(auto) f() { int i{ 0 }; return (i); }
My Visual Studio 2019 suggest me to remove redundant parenthesis, but actually they turn into decltype((i))
which changes return value to int&
which makes it UB since returning reference to a local variable.
Why are parentheses treated differently?
Parentheses aren't treated differently. It's the unparenthesized id-expression that's treated differently.
When the parentheses are present then the regular rules for all expressions apply. The type and value category are extracted and codified in the type of decltype
.
The special provision is there so that we could write useful code more easily. When applying decltype
to the name of a (member) variable, we don't usually want some type that represents the properties of the variable when treated as an expression. Instead we want just the type the variable is declared with, without having to apply a ton of type traits to get at it. And that's exactly what decltype
is specified to give us.
If we do care about the properties of the variable as an expression, then we can still get it rather easily, with an extra pair of parentheses.
Pre C++11 the language need tool(s) to get two different kinds of information:
Because of the nature of this information, the features had to be added in the language (it can't be done in a library). That means new keyword(s). The standard could have introduced two new keywords for this. For instance exprtype
to get the type of an expression and decltype
to get the declaration type of a variable. That would have been the clear, happy option.
However the standard committee has always tried its hardest to avoid introducing new keywords into the language in order to minimize breakage of old code. Backwards compatibility is a core philosophy of the language.
So with C++11 we got just one keyword used for two different things: decltype
. The way it differentiates between the two uses is by treating decltype(id-expression)
differently. It was a conscious decision by the committee, a (small) compromise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With