从重载函数中提取返回类型

11
我想提取函数的返回类型。问题是,有其他具有相同名称但不同签名的函数,我无法让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))?


2
你想要解决什么问题? - Lightness Races in Orbit
嗯,这有点不寻常。我正在尝试创建一个指针包装器模板——ref<T>,它为底层类提供操作符代理,从而使其(有点)像类。例如,如果String类具有 String operator + (const String& obj) 操作符,我希望ref<String>也有一个 String operator + (const String& obj) 操作符。参数(有限的数量)的推导已经解决了,因为它是一个简单的模板,但返回类型的推导是有问题的。 - Frigo
2
如果你使用的是C++11,std::ref已经实现了这个功能。它提供了一个操作符(Args...),可以正确地传递函数(或函数对象)的引用。它还具有隐式转换为T类型的引用。如果你没有使用C++11,我建议看一下boost::result_of的使用方法,了解它的工作原理。 - Dave S
你尝试过使用 boost::type_traitsboost::remove_pointer 吗? http://www.boost.org/doc/libs/1_47_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html - Tom Kerr
@Dave 我打算看看std::ref,它听起来正是我想要的,代理运算符和隐式转换。至少我可以得到一些实现自己想法的灵感。 - Frigo
3个回答

8

我认为可以使用decltypedeclval来实现:

例如: decltype(f(std::declval<T>())).


1
好的,它似乎没有调用它,但如果参数的类型没有默认构造函数,则仍然无法正常工作。 - Frigo
1
这并没有真正解决问题,因为它仍然需要一个默认构造函数... - Mankarse
2
@Tomalak:decltype指示符的操作数是未求值的操作数(7.1.6.2/4)。 - Steve Jessop
9
@Frigo @Mankarse <utility>中的declval解决了这个问题。如果你的编译器没有它,你可以自己做,使用template <typename T> typename std::add_rvalue_reference<T>::type declval() noexcept; - R. Martinho Fernandes
@Steve:哎呀,我知道的!> . < - Lightness Races in Orbit
显示剩余2条评论

1
检查没有参数的重载函数名称非常困难。您可以检查返回类型,以获取在元数上不同的重载--前提是没有任何元数有多个重载。即使如此,将一个硬错误(如果/当给定元数确实有多个重载)转换为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,我觉得有必要指出,除了typename std::result_of<Functor(Args...)>::type之外,我认为从不需要检查返回类型,而且这并不适用于函数名称;但也许您对此的兴趣纯粹是学术性的。


0

好的,经过几次尝试,我成功地解决了Mankarse建议的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;
}

编辑:

通过可变参数宏,我成功解决了这个问题,现在的语法是resultof(f, param1, param2, 等等)。如果没有可变参数宏,我无法将逗号传递到模板中的参数类型之间。我尝试使用resultof(f, (param1, param2, 等等))的语法,但没有成功。

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

该死,我无法使其与SFINAE兼容。有什么办法可以在函数不存在时防止错误? - Frigo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接