问题 如何获取C ++ bind(...)表达式的签名


我正在尝试编写一个名为signature_of的元函数,给定函数(指针),仿函数或lambda的类型,返回其签名。

这是我到目前为止所拥有的:

#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/function_types/is_member_function_pointer.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>

#include <type_traits>

template <typename F>
struct signature_of_member
{
    typedef typename boost::function_types::result_type<F>::type result_type;
    typedef typename boost::function_types::parameter_types<F>::type parameter_types;
    typedef typename boost::mpl::pop_front<parameter_types>::type base;
    typedef typename boost::mpl::push_front<base, result_type>::type L;
    typedef typename boost::function_types::function_type<L>::type type;
};

template <typename F, bool is_class>
struct signature_of_impl
{
    typedef typename boost::function_types::function_type<F>::type type;
};

template <typename F>
struct signature_of_impl<F, true>
{
    typedef typename signature_of_member<decltype(&F::operator())>::type type;
};

template <typename F>
struct signature_of
{
    typedef typename signature_of_impl<F, std::is_class<F>::value>::type type;
};

它非常简单,大多数实际工作都是由boost :: function_types库完成的。 一般的想法是:

  • 使用std :: is_class来区别对待 内置函数之间(包括 lambdas)和算子
  • 对于内置函数类型,使用boost :: function_types :: function_type来获取其签名
  • 对于仿函数,获取其运算符的类型(),获取其签名,并通过医生来删除“this”参数

这适用于内置函数:

int f(int);
typedef signature_of<decltype(f)>::type Sig;  // Sig is int(int)

对于lambdas:

auto f = [](int) { return 0; }
typedef signature_of<decltype(f)>::type Sig;  // Sig is int(int)

对于仿函数:

struct A
{
    int operator()(int);
};
typedef signature_of<A>::type Sig;  // Sig is int(int)

但是,它不适用于bind()表达式(这是仿函数的特例)。如果我试试这个:

#include <functional>
int g(int);
typedef signature_of<decltype(std::bind(g, 0))>::type Sig;

我收到编译器错误:

In file included from test.cpp:3:0:
signature_of.hpp: In instantiation of 'signature_of_impl<
        _Bind<int (*(int))(int)>, true
    >':
signature_of.hpp:45:74:   instantiated from 'signature_of<
        _Bind<int (*(int))(int)>
    >'
test.cpp:21:52:   instantiated from here
signature_of.hpp:39:74: error: type of '& _Bind<
        int (*)(int)({int} ...)
    >::operator()' is unknown

问题是bind()返回的仿函数的operator()是一个模板,因此无法确定其类型。

是否有可能以另一种方式获取bind()表达式的签名?


12266
2018-01-22 23:52


起源

VC10无法编译这个,错误通常是愚蠢的(error C2039: 'type' : is not a member of 'boost::function_types::result_type<T>'),它不喜欢这种专业化: typedef typename signature_of_member<decltype(&F::operator())>::type type; - Andriy Tylychko
@Andy T:如何在不提及std :: function的模板参数的情况下转换为std :: function 是 签名? - HighCommander4
是的,我的错,删除...... - Andriy Tylychko


答案:


你遇到的问题多于绑定器的operator()是模板化的,它还有一个任意的参数计数。请记住,您应该能够使用任意数量的额外参数调用bind的结果。例如:

int f(int);

auto bound = boost::bind(f, _2);

bound 现在可以使用任意数量的2或更多的参数调用,只有第二个实际转发到函数。

所以基本上,正如另一个答案所说,这个对象没有签名。它的签名仅由它的使用方式定义。


11
2018-01-23 00:24



好点,我实际上不知道你可以用这种方式使用bind()。 - HighCommander4


如果操作符是模板化的,那么它 没有 一个签名。


4
2018-01-22 23:58



或者看起来略有不同,它没有独特的签名。 operator() 可以使用不同的模板参数进行实例化,并且它们参与该实例化的签名 operator()。 - Steve Jessop
是的,但如果g有签名int(int),那么从概念上讲,bind(g,0)应该有签名int(void)。 std :: bind实现使用模板化运算符()的事实只是一个实现细节。 - HighCommander4
@ HighCommander4:从概念上讲?谁在乎?事实是,它不会发生。如果 bind 生成一个带有模板化运算符的仿函数(),你被搞砸了,你可以随心所欲地概念,而且它不会改变它。 - Puppy
当然,通过检查运算符()无法做到这一点,但也许还有另一种方法,例如通过检查bind()返回的对象的模板参数。 - HighCommander4
@ HighCommander4:MSDN表示返回类型未指定,只保证它提供了一个 result_type的typedef。事实上,运营商() 必须 模仿,以完美地转发它的论点。我刚检查了MSVC10,它认为返回类型没有任何用处。 - Puppy