Извлечение типа возврата из перегруженной функции - PullRequest
11 голосов
/ 31 августа 2011

Я хочу извлечь возвращаемый тип функции.Проблема в том, что есть другие функции с тем же именем, но с другой подписью, и я не могу заставить C ++ выбрать подходящую.Я знаю о 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))?

Ответы [ 3 ]

8 голосов
/ 31 августа 2011

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

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

1 голос
/ 31 августа 2011

Очень сложно проверить перегруженное имя функции без аргументов. Вы можете проверить типы возвращаемых данных на предмет перегрузок, которые отличаются по арности - при условии, что ни у одной арности нет более одной перегрузки. Даже тогда, превращение серьезной ошибки (если / когда заданная арность имеет более одной перегрузки) в 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;

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

0 голосов
/ 01 сентября 2011

Хорошо, после нескольких попыток мне удалось обойти метод std::declval, предложенный Манкарсе. Я использовал шаблон класса variadic для фиксации параметров и использовал шаблонное вычитание функций для получения возвращаемого значения из указателя функции. Его текущий синтаксис 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;
}
...