C++ decltype type descriptor

  • 2020-05-09 18:57:42
  • OfStack

1 basic grammar

The decltype type specifier generates the type of the specified expression. In the process, the compiler analyzes the expression and gets its type, but does not actually evaluate the expression.

Grammar:

decltype( expression )

The compiler USES the following rules to determine the type of the expression parameter.

If the expression parameter is accessed by an identifier or class member, decltype(expression) is the type of the entity named expression. If no such entity exists or if the expression parameter names 1 group of overloaded functions, the compiler generates 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. The parentheses around the overloaded operator are ignored.
If the expression parameter is an rvalue, decltype(expression) is of type expression. If the expression parameter is an lvalue, decltype(expression) is an expression of the lvalue reference type.
The following sample code is given:


int var;
const int&& fx(); 
struct A { double x; }
const A* a = new A();

Statement   type   comment
decltype (fx ());   const int &&   references const int to the left value
decltype (var); The type of the   int   variable var
decltype(a- > x);   double   member access type
decltype((a- > x));   const double&   inner brackets cause statements to access the evaluation as expressions rather than as members. Since a is declared as an const pointer, the type is a reference to const double.

2 decltype and references

If the expression used by decltype is not a variable, decltype returns the type of the result of the expression. Sometimes, however, some expressions return a reference type to decltype. 1 generally, when this happens, it means that the result object of the expression can be used as the lvalue of an assignment statement:


// decltype The result can be a reference type 
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; // OK,  The result of addition is int , so b is 1 One (uninitialized) int
decltype(*p) c; // Error, c is int& .   Must be initialized 

Since r is a reference, the result of decltype(r) is a reference type. If you want the result type to be the type referred to by r, you can use r as part 1 of the expression, such as r+0. Obviously, the result of this expression will be a specific value rather than a reference.

On the other hand, if the content of the expression is a de-reference operation, decltype will get the reference type. As we are familiar with, the dereference pointer can get the pointer to the object and assign a value to it, so the result type of decltype(*p) is int& instead of int.

3 decltype and auto

Top-level const and references are handled differently (reference: C++ auto type descriptor)
If the expression used by decltype is a variable, decltype returns the type of that variable (including top-level const and references) :


const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x Is of type const int
decltype(cj) y = x; // y Is of type const int& . y Bind to a variable x
decltype(cj) z; // Error .  z is 1 A reference must be initialized 

The result type of decltype is closely related to the form of the expression

For the reference used by decltype, if the variable name is bracketed with a 1 pair, the resulting type will be different than if the variable name is not bracketed. If decltype USES an unbracketed variable, the result is the type of the variable. If you add one or more parentheses to a variable, the compiler will treat it as an expression.


decltype((i)) d; // Error, d is int&,  Must be initialized 
decltype(i) e;  // OK, e is 1 uninitialized int

The return type of the template function

In C++11, you can use a combination of the decltype type specifier on the trailing return type and the auto keyword to declare a template function whose return type depends on its template parameter type.
In C++14, you can use decltype(auto) without trailing return types to declare template functions whose return types depend on their template parameter types.
For example, define a summation template function:


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

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

(forward: if the parameter is an rvalue or rvalue reference, conditionally cast the parameter to an rvalue reference.)

Attached is a paragraph of source code:


#include <iostream>
#include <string>
#include <utility>
#include <iomanip>

using namespace std;

template<typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) -> 
  decltype(forward<T1>(t1) + forward<T2>(t2))
{
  return forward<T1>(t1) + forward<T2>(t2);
}

class X
{
  friend X operator+(const X& x1, const X& x2)
  {
   return X(x1.m_data + x2.m_data);
  }

public:
  X(int data) : m_data(data) {}
  int Dump() const { return m_data;}
private:
  int m_data;
};

int main()
{
  // Integer 
  int i = 4;
  cout << 
   "Plus(i, 9) = " << 
   Plus(i, 9) << endl;

  // Floating point
  float dx = 4.0;
  float dy = 9.5;
  cout <<  
   setprecision(3) << 
   "Plus(dx, dy) = " <<
   Plus(dx, dy) << endl;

  // String   
  string hello = "Hello, ";
  string world = "world!";
  cout << Plus(hello, world) << endl;

  // Custom type
  X x1(20);
  X x2(22);
  X x3 = Plus(x1, x2);
  cout << 
   "x3.Dump() = " << 
   x3.Dump() << endl;
}

The running result is:


Plus(i, 9) = 13
Plus(dx, dy) = 13.5
Hello, world!
x3.Dump() = 42


Related articles: