decltype in C++: Understand the details

Share the Article

decltype

The keyword decltype in C++, returns the data type of an expression or variable. This keyword works for any type, built-in or user-defined. Following example illustrates this behavior.

#include <iostream> //main header using namespace std; //namespace class MFPoint { }; int main() { int ia; //Built-in type cout << typeid(decltype(ia)).name() << endl; double db; //Built-in type cout << typeid(decltype(db)).name() << endl; MFPoint mfp; //User-defined type cout << typeid(decltype(mfp)).name() << endl; return 0; }

The output is:

i d 7MFPoint

Reference type information

The decltype also returns the information regarding reference. For instance, if the given input is a variable of type L-value reference, then decltype takes care of this. Following example, illustrates this with a normal variables and with both an L-value and and R-value reference.

#include <iostream> //main header using namespace std; //namespace class MFPoint { }; int main() { int ia0; //Not reference variable if(std::is_reference<decltype(ia0)>() ) cout << "Reference" << endl; else cout << "No reference" << endl; int &ia = ia0; //L-value reference if(std::is_reference<decltype(ia)>() ) cout << "Reference" << endl; else cout << "No reference" << endl; double &&db=1.1; //R-value reference if(std::is_reference<decltype(db)>() ) cout << "Reference" << endl; else cout << "No reference" << endl; MFPoint mfp0; MFPoint &mfp = mfp0; if(std::is_reference<decltype(mfp)>() ) cout << "Reference" << endl; else cout << "No reference" << endl; return 0; }

Output

No reference Reference Reference Reference

Additionally, the following program verifies that decltype is returning L-value and R-value reference information.

#include <iostream> //main header using namespace std; //namespace int main() { int ia0; if( std::is_reference<decltype(ia0)>() ) cout << "Reference" << endl; else cout << "No Reference" << endl; int &ia = ia0; if( std::is_reference<decltype(ia)>() ) { if(std::is_rvalue_reference<decltype(ia)>() ) cout << "R-value Reference" << endl; else cout << "L-rvalue Reference" << endl; } double &&db=1.1; if( std::is_reference<decltype(db)>() ) { if(std::is_rvalue_reference<decltype(db)>() ) cout << "R-value Reference" << endl; else cout << "L-rvalue Reference" << endl; } return 0; }

Output is as follows:

No Reference L-rvalue Reference R-value Reference

auto return type without trailing return type

In C++11, the main use of decltype is for the declaration of functions where function return type is auto. Therefore, in such cases, a trailing return type expressions guide the compiler to deduce the return type. For this reason, in the absence of a trailing type, the compiler shall generate error.

For instance, in the following example, there is no trailing return type for function “getValue”.

#include <iostream> //main header using namespace std; //namespace auto getValue(int ix) { int iy = ix; return iy; } int main() { return 0; }

The output from compiler is:

error due to non-usage of training return type from function

Following code show, how to add a trailing return type. Generally, the trailing type uses the type of parameters. In particular, this is very much useful in template functions.

auto getValue(int ix) -> decltype(ix) { int iy = ix; return iy; }

If the trailing return type expression is constructed using some variable which is not a parameter, then the compiler shall generate error. For example, in this code, the variable iy is inside in the function scope but not in parameters.

auto getValue(int ix) -> decltype(iy) { int iy = ix; return iy; }
compiler error due to incorrect trailing return type spec

Constructing trailing return type for template functions

#include <iostream> //main header using namespace std; //namespace template<typename T> auto getValue(T ix) -> decltype(ix) { T iy = ix; return iy; } int main() { getValue(4); return 0; }

Use in variable definition

Since, the decltype returns actual datatype of variable, therefore, such datatype can be used to define a variable.

The following example illustrates this:

#include <iostream> //main header using namespace std; //namespace int main() { int ix = 9; auto iy = ix; decltype(ix) x1; //x1 is int decltype(iy) y1; //y1 is int cout << typeid(x1).name() << endl; cout << typeid(y1).name() << endl; return 0; }

The output clearly shows both x1 and y1 become “int” type variable.

i i

decltype for L-value expressions

When we use decltype with complicated L-value expressions, the this returns L-value types. For example, decltype(x) is int, whereas, when this the name x is wrapped in parenthesis, (x), then it becomes an L-value expression. Therefore, now decltype((x)) shall return int&.

#include <iostream> //main header using namespace std; //namespace int main() { int iy; decltype(iy) iz = 8; // iy is int decltype((iy)) ik = iz; // (iy) is int& if(std::is_reference<decltype(iz)>()) cout << "Reference" << endl; else cout << "No Reference" << endl; if(std::is_reference<decltype(ik)>()) cout << "Reference" << endl; else cout << "No Reference" << endl; return 0; }

Output:

No Reference Reference

Main Funda: decltype always return type of the expression.

Related Topics:

What are dependent scope type in templates?
What is an implicit interface? 
Calling member functions in template base class
What is template meta programming (TMP)
How delete keyword can be used to filter polymorphism
Concept of Inline Functions
What is reference collapsing?
How std::forward( ) works?
How std::move() function works?
Smart Pointers: shared_ptr <T> 
Smart Pointers: unique_ptr<T>
What is move constructor ?
Understanding Constant Variables


Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *