decltype feature in C++11/C++14/C++17

C-Plus-Plus

In this article, we are going to learn about the use of decltype in C++. We will be covering this topic with C++ 11, C++14, and C++ 17 updates. The decltype specifier is another new feature introduced in C++11. decltype helps us to evaluate the type of expression or entity.

So,in short we can say that decltype “returns” a type.

Now let us understand this with the example and explaination below.

So we begin with the syntax for the decltype.

Syntax for decltype

decltype ( entity )	
or
decltype ( expression )	

As you can see above that two syntaxes are available. If you see the first syntax which is decltype ( entity ), Here entity is the name of a variable, a function or denotes a member of an object,and decltype will return the type of whatever is the type of this entity. If that entity is not declared then the compiler will give you an error.

Let us see some examples:

class SampleClassA {
   int var1;
 };

 SampleClassA obj1;
 decltype(obj1) obj2; 

If you see the above example where we are creating an object of class SampleClassA type as obj1.Then we are creating another object of the same class but this time we are using decltype to determine the object type.

If we try to understand the below line of code what it is doing is, First decltype(obj1) will return us SampleClassA and then will create the
object of type SampleClassA as obj2.

 decltype(obj1) obj2;  

The second syntax where we have an expression as input. We have below rules to support:

If the expression which is our input parameter is a function call, then decltype will return us the type as the return type of the function that we are calling.
Here is an example to understand this:
We have a function add() which have a return type of float.

float add(int a, int b);

decltype(add())

Here decltype will return float which is the return type of the function.

Use of Parentheses

The use of brackets also changes the behavior and output of the decltype. Let us understand this with the example below.

const SampleClassA* point = new SampleClassA();

decltype(point->var1)     // decltype(entity) // Line 1
decltype((point->var1))   // decltype( expression) // Line 2

If you see the output of above both lines of code, you will be surprised to see different results.
For line 1, the output is int which is a type of point->var1. But for line 2 the output will be const int&.


Why is this change in behavior???

If you check the code, the difference that you can see is parentheses. With the addition of parentheses the statement to be evaluated as an expression instead of member access.
Now the type of point, which is const pointer made the return type of decltype to be a reference to const int means const int&.

Some more rules apply to the expression type input parameters to decltype such as:

If the input argument is any other expression of type T, and

  • if input expression is xvalue, then decltype returns T&&;
  • if input expression is an lvalue, then decltype returns T&;
  • if input expression is prvalue, then decltype returns T.

C++14

With the introduction of C++ 14, we got one more special case of decltype which is decltype(auto). decltype(auto) returns the type of a variable from its initializer value.
In the case of functions, it returns the return type of the value the function is returning as per function definition. In both cases, all rules of decltype type deduction get applied and auto rules get overridden.

Let us understand this with examples:

int var1 = 100;
auto var2 = var1;    // var1 is of type int because var1 is int.
// var3 is of type int because decltype(auto) will return the type of var1 which is int.
decltype(auto) var3 = var1; 

Another improvement we got in C++14 is we don’t need to mention decltype in trailing return type with auto.
Now you can do that upfront as decltype(auto)

In C++ 11

//In C++ 11
template<typename T, typename U>
auto add(T&& t, U&& u) -> decltype 
        { return t + u; }

In C++ 14

// In C++ 14
template<typename T, typename U>
decltype(auto) add(T&amp;&amp; t, U&amp;&amp; u)
        { return t + u; }

C++ 17 – Exception

With the introduction of the C++17 feature of structured binding. This decltype syntax required some clarifications. So as an exception to C++ 17 structured binding, If the entity(variable, a function or denotes a member of an object) that we pass to decltype as input is one of the variables defined in structured binding, then decltype returns the referenced type as described in the specification of the structured binding declaration.

To understand this let us see an example.

We have a pair as structured binding of two variable

std::pair<int &&, float&> var(int);
auto const& [var1, var2] = var3(10);
decltype(var1)

Here you can see that the type of var1 is int const&, but when we try to check the decltype(var1) we got the return type as int && which is the type of the first element of pair.so in the special case of structured binding the decltype return the type as the original binding variable type, not the assigned value type.