Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

How to Let the Compiler Declare the Type of a Variable for Later Initialization by a Function

3.00/5 (2 votes)
4 May 2020CPOL1 min read 7.8K   30  
This tip demonstrates the use of the STL template std::invoke_result
The template std::invoke_result defined in when used with decltype allows the compiler to deduce the type of a function return via its name any template parameters and the types of its arguments. This is useful when needing to declare a variable which will be initialized later in the file by a particular function.

The template std::invoke_result (since C++17) defined in <type_traits> when used with decltype allows the compiler to deduce the return type of a function or method via its name its class name template parameters and the types of its arguments. This is useful when needing to declare a variable which will be initialized later in the file by a particular function or method.

You of course know about and utilize auto to declare the type of a variable when also initializing it with a function or method call. However if you happen to need to declare such a variable without first such initialization the template std::invoke_result (since C++17) defined in <type_traits> is helpful. When used with decltype it permits declaring the type of a variable based on the name of a particular function or method via its name its class name template parameters and types of its arguments so no need to concern yourself with precise knowledge of the return type. Omitting the legal details for functions it is used as

using return_type = std::invoke_result<decltype(templated function name), type of arg1, etc.>::type;

For methods it is used as

using return_type = std::invoke_result<decltype(&templated class name::templated function name), templated class name, type of arg1, etc.>::type;

The code below demonstrates this with five examples of increasing complexity. The first example demonstrates its use for a function with no arguments. The fourth example demonstrates that if the return type does not depend on a template parameter then it does not matter that the function template parameter utilized within decltype is not the same as that utilized when actually calling the function. The last example demonstrates its use for a method.

#include <iostream>
#include <vector>
#include <type_traits>

using namespace std;

template<class T>
class cfoobar
{
public:
	double foobar(int) { return 0.; }
};

int foobar() { return 0; }
using foobar_return_type = invoke_result<decltype(foobar)>::type;

int goobar(int&, double*) { return 0; }
using goobar_return_type = invoke_result<decltype(goobar), int&, double*>::type;

template<class T>
T hoobar(T) { return T(); }
using hoobar_return_type = invoke_result<decltype(hoobar<cfoobar<int>>), cfoobar<int>>::type;

template<class T>
vector<int> koobar(T, double) { return vector<int>(); }
using koobar_return_type = invoke_result<decltype(koobar<cfoobar<int>>), cfoobar<int>, double>::type;

using cfoobar_foobar_return_type = invoke_result<decltype(&cfoobar<int>::foobar), cfoobar<int>, int>::type;

int main()
{
	cout << typeid(foobar_return_type).name() << endl;
	cout << typeid(goobar_return_type).name() << endl;
	cout << typeid(hoobar_return_type).name() << endl;
	cout << typeid(koobar_return_type).name() << endl;
	cout << typeid(cfoobar_foobar_return_type).name() << endl;
}

The output shown below demonstrates its correctness.

int
int
class cfoobar<int>
class std::vector<int,class std::allocator<int> >
double

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)