Извлечение возвращаемого типа из перегруженной функции

Я хочу извлечь возвращаемый тип функции. Проблема в том, что есть другие функции с тем же именем, но с другой подписью, и я не могу заставить С++ выбрать соответствующий. Я знаю о std:: result_of, но из нескольких попыток я пришел к выводу, что он страдает от одной и той же проблемы. Я также слышал о решении, включающем decltype, но я не знаю никаких особенностей.

В настоящее время я использую метапрограммирование шаблона для извлечения типа возвращаемого значения из типа указателя функции, который отлично работает для ограниченного числа параметров (любое неограниченное решение?), учитывая, что извлечение типа указателя функции работает для однозначного функции.

#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;

}

Любая идея, какая синтаксическая функция отсутствует, и как ее исправить, желательно с решением, которое работает простым способом, например. resultof(f(int))?

Ответ 1

Я думаю, что это можно сделать с помощью decltype и declval:

Например: decltype(f(std::declval<T>())).

Ответ 2

Очень сложно проверить перегруженное имя функции без аргументов. Вы можете проверить типы возврата для перегрузок, которые различаются по arity - при условии, что arity не имеет более одной перегрузки. Даже тогда, когда жесткая ошибка (если/когда заданная артерия имеет более одной перегрузки) в SFINAE - это боль, поскольку она требует записи признака только для этой конкретной функции (!), Поскольку перегруженные имена функций не могут передаваться как любой аргумент. Возможно также, чтобы пользовательский код использовал явную специализацию...

template<typename R>
R
inspect_nullary(R (*)());

template<typename R, typename A0>
R
inspect_unary(R (*)(A0));

int f();
void f(int);

int g();
double g();

typedef decltype(inspect_nullary(f)) nullary_return_type;
typedef decltype(inspect_unary(f)) unary_return_type;

static_assert( std::is_same<nullary_return_type, int>::value, "" );
static_assert( std::is_same<unary_return_type, void>::value, "" );

// hard error: ambiguously overloaded name
// typedef decltype(inspect_nullary(g)) oops;

Учитывая, что вы используете С++ 0x, я чувствую необходимость указывать на то, что (IMO) никогда не нужно проверять тип возврата за пределами typename std::result_of<Functor(Args...)>::type, и это не относится к именам функций; но, возможно, ваш интерес к этому является чисто академическим.

Ответ 3

Хорошо, после нескольких попыток мне удалось обойти метод std::declval, предложенный Манкарсе. Я использовал шаблон вариационного класса для фиксации параметров и использовал шаблонный вывод функций для получения возвращаемого значения из указателя функции. Его текущий синтаксис typeof(ResultOf<parameters>::get(function)), к сожалению, он еще далек от желаемой формы resultof<parameters>(function). Измените этот ответ, если я найду способ упростить его.

#include <iostream>
#include <typeinfo>

using namespace std;

template <class... Args>
class ResultOf
{
    public:
        template <class R>
        static R get (R (*) (Args...));
        template <class R, class C>
        static R get (R (C::*) (Args...));
};

class NoDefaultConstructor
{
    public:
        NoDefaultConstructor (int) {}
};

int f ();
double f (int x);
bool f (NoDefaultConstructor);
int f (int x, int y);


int main (int argc, char* argv[])
{
    if(argc||argv){}

    cout << typeid(typeof(ResultOf<>::get(f))).name() << endl;
    cout << typeid(typeof(ResultOf<int>::get(f))).name() << endl;
    cout << typeid(typeof(ResultOf<NoDefaultConstructor>::get(f))).name() << endl;
    cout << typeid(typeof(ResultOf<int, int>::get(f))).name() << endl;

    typeof(ResultOf<int>::get(f)) d = 1.1;
    cout << d << endl;
}

Edit:

Чтобы решить эту проблему с помощью переменных массивов, синтаксис теперь resultof(f, param1, param2, etc). Без них я не мог передавать запятые между типами параметров в шаблон. Пробовал синтаксис resultof(f, (param1, param2, etc)) безрезультатно.

#include <iostream>

using namespace std;

template <class... Args>
class Param
{
    public:
        template <class R>
        static R Func (R (*) (Args...));
        template <class R, class C>
        static R Func (R (C::*) (Args...));
};

#define resultof(f, ...) typeof(Param<__VA_ARGS__>::Func(f))

int f ();
double f (int x);
int f (int x, int y);

int main (int argc, char* argv[])
{
    resultof(f, int) d = 1.1;
    cout << d << endl;
}