I want to extract the return type of a function. Problem is, there are other functions with the same name but different signature, and I can not get C++ to select the appropriate one. I know about std::result_of, but from a few tries I have concluded it suffers from the same problem as well. I have heard about a solution involving decltype as well, but I do not know any specifics.
At the moment I am using template metaprogramming to extract the return type from a function pointer type, which works fine for a limited number of parameters (any non-limited solution?), given that extraction of function pointer type works for unambiguous functions.
#include <iostream>
using namespace std;
// ----
#define resultof(x) typename ResultOf<typeof(x)>::Type // might need a & before x
template <class T>
class ResultOf
{
public:
typedef void Type; // might need to be T instead of void; see below
};
template <class R>
class ResultOf<R (*) ()>
{
public:
typedef R Type;
};
template <class R, class P>
class ResultOf<R (*) (P)>
{
public:
typedef R Type;
};
// ----
class NoDefaultConstructor
{
public:
NoDefaultConstructor (int) {}
};
int f ();
int f ()
{
cout << "f" << endl;
return 1;
}
double f (int x);
double f (int x)
{
cout << "f(int)" << endl;
return x + 2.0;
}
bool f (NoDefaultConstructor);
bool f (NoDefaultConstructor)
{
cout << "f(const NoDefaultConstructor)" << endl;
return false;
}
int g ();
int g ()
{
cout << "g" << endl;
return 4;
}
int main (int argc, char* argv[])
{
if(argc||argv){}
// this works since there is no ambiguity. does not work without &
// resultof(&g) x0 = 1;
// cout << x0 << endl;
// does not work since type of f is unknown due to ambiguity. same thing without &
// resultof(&f) x1 = 1;
// cout << x1 << endl;
// does not work since typeof(f()) is int, not a member function pointer; we COULD use T instead of void in the unspecialized class template to make it work. same thing with &
// resultof(f()) x2 = 1;
// cout << x2 << endl;
// does not work per above, and compiler thinks differently from a human about f(int); no idea how to make it correct
// resultof(f(int)) x3 = 1;
// cout << x3 << endl;
// does not work per case 2
// resultof(f(int())) x4 = 1;
// cout << x4 << endl;
// does not work per case 2, and due to the lack of a default constructor
// resultof(f(NoDefaultConstructor())) x5 = 1;
// cout << x5 << endl;
// this works but it does not solve the problem, we need to extract return type from a particular function, not a function type
// resultof(int(*)(int)) x6 = 1;
// cout << x6 << endl;
}
Any idea what syntax feature am I missing and how to fix it, preferably with a solution that works in a simple way, e.g. resultof(f(int))
?
Okay, after a few attempts I managed to work around the
std::declval
method suggested by Mankarse. I used a variadic class template to fixate the parameters, and used the template deduction of functions to get the return value from a function pointer. Its current syntax istypeof(ResultOf<parameters>::get(function))
, unfortunately it is still far from the desiredresultof<parameters>(function)
form. Will edit this answer if I find a way to further simplify it.Edit:
Managed to solve it with variadic macros, the syntax is now
resultof(f, param1, param2, etc)
. Without them I couldn't pass the commas between the parameter types to the template. Tried with the syntaxresultof(f, (param1, param2, etc))
to no avail.I think that this can be done with
decltype
anddeclval
:For example:
decltype(f(std::declval<T>()))
.It's very hard to inspect an overloaded function name without arguments. You can inspect the return types for overloads that differ in arity -- provided that no arity has more than one overload. Even then, turning a hard error (if/when a given arity does have more than one overload) into SFINAE is a pain as it requires writing a trait just for that particular function(!) since overloaded function names can't be passed as any kind of argument. Might as well require user code to use an explicit specialization...
Given that you're using C++0x, I feel the need to point out that there is (IMO) never a need to inspect a return type beyond
typename std::result_of<Functor(Args...)>::type
, and that doesn't apply to function names; but perhaps your interest in this is purely academical.