C++: decltype

decltype is a keyword introduced in C++11 that infers the type of a given expression. Although it can be used instead of auto, it is mainly used for return types.

For example, this function:

int sum(int a, int b)
{
  using ret = int;
  ret x = a + b;
  return x;
}

could be written using decltype:

int sum(int a, int b)
{
  using ret = decltype(a + b);
  ret x = a + b;
  return x;
}

The compiler evaluates the types of a + b and infers that the type of the resulting expression is int, and aliases ret as an int.

Useful? In the case shown above: not so much.

But what if someone wants the compiler to infer the return type of the method shown above? What if it is written as follows:

auto sum(int a, int b)  // does not compile before C++14!!!
{
  return a + b;
}

Though it appears to be correct, auto was not supported for return types before C++14, so the example above did not use to compile.

decltype could be used:

decltype(a + b) sum(int a, int b) // this does not compile
{
  return a + b;
}

Though the decltype syntax shown in the example above is correct and works in the first example, in this case it does not compile because when parsing the decltype(a + b) construction, the compiler does not know what a and b are.

To solve this problem, in C++11 there is a new way to declare functions and methods:

auto sum(int a, int b) -> int
{
  return a + b;
}

In the example shown above, with the auto keyword, the coder is telling the compiler that the return type will be specified later, and “later” means after the -> in the function declaration. Using this syntax, the programmer can use decltype to specify the return type:

auto sum(int a, int b) -> decltype(a+b)
{
  return a + b;
}

Useful? With my example, it is still not very useful; however, the new syntax allows the programmer to use decltype in more complex function declarations, and this new syntax is also useful for declaring lambda expressions.

decltype becomes really useful when it is used with templates.

Consider a method template called execute that receives as parameters the pointer to the function to execute and a value to pass to that function:

template <typename T, typename F> // does not compile
??? execute(const T& val, F func)
{
  return func(val);
}

What would be the return type of that function? Consider these functions:

std::string getname(int x)
{
  switch (x)
  {
    case 0: return "zero";
    case 1: return "one";
    case 2: return "two";
  }
  return "I need to learn to count";
}

and

int getlength(const std::string& s)
{
  return s.length();
}

The programmer would want to use the ‘execute‘ function in this way:

std::cout << execute("hello", getlength) << endl;
std::cout << execute(1, getname) << endl;

To solve this problem, the method template can be declared using decltype:

template <typename T, typename F>
auto execute(const T& t, F func) -> decltype(func(t))
{
  return func(t);
}

So in this case, decltype is very useful, and without it, implementing this kind of functionality would have been impossible before C++14.

Since C++14 introduced automatic return type deduction using auto, the practical application of decltype is significantly reduced.

More posts about auto:

5 thoughts on “C++: decltype

  1. nicely explained :) in the post u talk about using the decltype with lambdas could you kindly add an example of that?

    thanks i love such articles where by way of examples effective instruction is achieved :)

    1. Thanks awhan for your nice comments.

      About using decltype with lambda expressions, you can explicitly mark the return type of your lambda expression.

      Consider this example:

      class Chloride { };
      
      class Salt { };
      
      class Sodium
      {
      	public:
      		Salt operator+(const Chloride& x) const
      		{
      			return Salt();
      		}
      		
      };
      
      ostream& operator<<(ostream& os, const Salt&)
      {
      	os << "Salt";
      	return os;
      }
      
      template <typename T>
      void show(const T& t)
      {
      	cout << t() << endl;
      }
      
      int main()
      {
      	Sodium na;
      	Chloride cl;
      	
      	show([na, cl]() -> decltype(na + cl)
      	{
      		return na + cl;
      	});
      	return 0;
      }
      

      Look at the show() invocation, you are passing it a lambda expression and defining explicitly its return type.

  2. Hey, thanks for taking the time to write this down. However, I want to add that the last example you give is a bit odd in the sense that you neither need ‘decltype’ nor ‘auto’ to achieve this task. In fact, you can rewrite the execute template as follows (which compiles without using any features of the C++11 standard).

    // template <typename ArgType, typename FunType>
    // auto execute(ArgType arg, FunType fun) -> decltype(fun(arg)) {
    //   return fun(arg);
    // }
    
    template <typename ArgType, typename RetType>
    RetType execute(ArgType arg, RetType (*fun)(ArgType)) {
      return fun(arg);
    }
    
    1. Hi Michael. Thanks for your comment.

      Yes, your implementation works perfectly without using any C++11 features but it will only work with function pointers, not with functors or lambda expressions.

      Am I correct?

Leave a reply to ebasconp Cancel reply