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

发布于 2024-12-02 14:20:58 字数 2563 浏览 0 评论 0原文

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

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))?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

桃扇骨 2024-12-09 14:20:58

我认为这可以通过 decltypedeclval 来完成:

例如: decltype(f(std::declval()))

I think that this can be done with decltype and declval:

For example: decltype(f(std::declval<T>())).

美胚控场 2024-12-09 14:20:58

在没有参数的情况下检查重载函数名称非常困难。您可以检查返回类型是否有不同数量的重载——前提是没有一个数量具有多个重载。即便如此,将硬错误(如果/当给定的数量确实有多个重载时)转换为 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::type,这不适用于函数名称;但也许你对此的兴趣纯粹是学术上的。

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...

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;

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.

暮倦 2024-12-09 14:20:58

好的,经过几次尝试,我设法解决了 Mankarse 建议的 std::declval 方法。我使用可变参数类模板来固定参数,并使用函数的模板推导从函数指针获取返回值。它当前的语法是 typeof(ResultOf::get(function)),不幸的是,它距离所需的 resultof(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, 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;
}

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 is typeof(ResultOf<parameters>::get(function)), unfortunately it is still far from the desired resultof<parameters>(function) form. Will edit this answer if I find a way to further simplify it.

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

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 syntax resultof(f, (param1, param2, etc)) to no avail.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文