如何确定可调用对象的函数签名
How to determine function signature of a callable object?
有没有办法在C++中确定可调用对象的函数签名?
请考虑以下事项:
template< typename F >
void fun(F f)
{
// ...
}
假设fun
仅使用可调用的"事物"进行调用。
在fun
里面,我想知道函数f
的签名是什么。这应该适用于函数指针、引用、包装器、lambda、绑定、函数对象(前提是它们只有一个operator ()
)等。我对Visual Studio 2010 SP 1有限制,但即使不在该编译器上工作,我也对标准解决方案感兴趣。
(函数签名Return_Type ([Arg1_Type [, Arg2_Type [, ... ] ] ])
;与std::function
/boost::function
相同。
至少知道f
的返回值的部分解决方案具有一定的价值。(我已经尝试过std::result_of
但在我尝试过的任何情况下都无法让它工作。
在 C++0x 兼容编译器上,您至少可以使用 decltype(f())
获取 f()
的结果类型。Visual C++ 2010 应该支持 decltype,尽管我自己还没有检查过。至于获取参数类型,我不确定是否有一种方法可以使用函数指针。
编辑
Boost.Function似乎已经弄清楚了,至少在某些编译器上是这样(例如,它不适用于旧版本的VC++或Borland C++)。它可以包装函数指针并为其提取参数。然而,该解决方案似乎相当复杂,它涉及使用 Boost.PP 定义多个模板。如果你想尝试重新实现所有内容,你当然可以尝试,但我认为你也可以使用一个虚拟的 Boost.Function 包装器来让事情变得更容易,例如 boost::function<decltype(f)>::second_argument_type
获取第二个参数类型。
你可以看看提升函数类型:
http://www.boost.org/doc/libs/1_46_1/libs/function_types/doc/html/boost_functiontypes/introduction.html
在尝试解决此问题时,我想出了以下部分解决方案:
#include <cstdlib>
#include <functional>
#include <iostream>
#include <typeinfo>
#include <boost/bind.hpp>
#include <boost/function.hpp>
template< typename T >
struct identity
{
typedef T type;
};
// ----------
// Function signature metafunction implementation
// Also handler for function object case
// ----------
template< typename T >
struct function_signature_impl
: function_signature_impl< decltype( &T::operator() ) >
{
};
// ----------
// Function signature specializations
// ----------
template< typename R >
struct function_signature_impl< R () >
: identity< R () >
{
};
template< typename R, typename A1 >
struct function_signature_impl< R ( A1 ) >
: identity< R ( A1 ) >
{
};
template< typename R, typename A1, typename A2 >
struct function_signature_impl< R ( A1, A2 ) >
: identity< R ( A1, A2 ) >
{
};
// ----------
// Function pointer specializations
// ----------
template< typename R >
struct function_signature_impl< R ( * )() >
: function_signature_impl< R () >
{
};
template< typename R, typename A1 >
struct function_signature_impl< R ( * )( A1 ) >
: function_signature_impl< R ( A1 ) >
{
};
// ----------
// Member function pointer specializations
// ----------
template< typename C, typename R >
struct function_signature_impl< R ( C::* )() >
: function_signature_impl< R () >
{
};
template< typename C, typename R, typename A1 >
struct function_signature_impl< R ( C::* )( A1 ) >
: function_signature_impl< R ( A1 ) >
{
};
template< typename C, typename R >
struct function_signature_impl< R ( C::* )() const >
: function_signature_impl< R () >
{
};
template< typename C, typename R, typename A1 >
struct function_signature_impl< R ( C::* )( A1 ) const >
: function_signature_impl< R ( A1 ) >
{
};
// ----------
// Function signature metafunction
// ----------
template< typename T >
struct function_signature
: function_signature_impl< T >
{
};
// ----------
// Tests
// ----------
template< typename F >
void test( F f )
{
typedef function_signature< F >::type signature_type;
std::cout << typeid( F ).name() << std::endl;
std::cout << 't' << typeid( signature_type ).name() << std::endl;
std::cout << std::endl;
}
int foo( int )
{
return 0;
}
struct bar
{
int operator ()( int )
{
return 0;
}
};
struct cbar
{
int operator ()( int ) const
{
return 0;
}
};
struct abar1
{
int operator ()( int ) const
{
return 0;
}
int operator ()( int )
{
return 0;
}
};
struct abar2
{
int operator ()( int )
{
return 0;
}
int operator ()( double )
{
return 0;
}
};
struct mem
{
int f( int ) const
{
return 0;
}
};
int main()
{
test(
[]( int ) -> int { return 0; }
);
test(
foo
);
test(
&foo
);
test(
bar()
);
test(
cbar()
);
test(
std::function< int ( int ) >( &foo )
);
test(
boost::function< void ( int ) >( &foo )
);
/*
test(
std::bind( &mem::f, mem(), std::placeholders::_1 )
);
*/
/*
test(
boost::bind( &mem::f, mem(), _1 )
);
*/
/*
test(
abar1()
);
*/
/*
test(
abar2()
);
*/
return EXIT_SUCCESS;
}
(没有添加用于再次检查不正确参数的代码。
这个想法是,function_signature< decltype( f ) >::type
应该是f( ... )
的签名,其中"......"是签名。这尤其意味着指向成员函数的指针在这里是一个无效的参数(尽管代码没有对此进行检查),因为这样的指针不能直接"调用"。
最后是失败的测试(在VS 2010中)。这一切都是由于operator ()
超载。这使得该代码大多无用,因为它不适用于 bind
的结果。但也许它可以进一步发展。
回答安德烈·伯格纳的问题:
function_signature_impl
从来都不是从自身衍生出来的。它是一个类型模板,仅表示实际类型的松散耦合族。但实际的类型(即使你属于同一个家族)是不同的类型。
显然,&T::operator()
是指向类型为T
的呼叫运算符(operator()
)的指针。基本上只是一个成员函数指针(其中成员函数恰好是调用运算符)。虽然decltype
是该指针的类型。这可能看起来微不足道(尤其是两者type_info::name
显示相同),但对于模板来说,这确实很重要,因为一个是指针,而另一个是类型(显然)。
需要此"案例"来覆盖函子(对象"可调用"的类型)。请注意,仅当模板参数T
与列出的"案例"中的其他任何参数不匹配时,才会使用此非专用function_signature_impl
。
我希望在这么长时间之后我做对了。虽然我不确定我是否真正完全理解过它。代码是实验的结果。
这个答案是 SlashLife 在 freenode ##c++ 上给我的:
template <typename T, typename Signature>
struct signature_impl;
template <typename T, typename ReturnType, typename... Args>
struct signature_impl<T, ReturnType(T::*)(Args...)>
{
using type = ReturnType(Args...);
};
template <typename T>
using signature_t = signature_impl<T, decltype(&T::operator())>;
需要注意的是,它仅在存在唯一operator()
时才有效,并且不适用于 lambda。
您可以使用std::is_invocable_r
- 如何使用单独文件中的派生类访问友元函数对象
- 当使用透明的std函数对象时,我们还需要写空的尖括号吗
- 有没有办法将重载的类函数绑定到函数对象?
- 将指针传递到成员的指针,从模板参数包到函数对象
- 如何在类模板的成员函数中正确调用函数对象?正在生成 Visual Studio 编译器错误 C2440
- 隐式转换为比较函数对象(函子)用于 std::sort 而不是 std::map?
- C++使用函数对象的线程,如何调用多个析构函数而不是构造函数?
- 如何通过接口将函子分配给函数对象
- 对std::函数对象的调用不匹配,该对象是指向成员函数的指针
- 指向std::invoke中成员函数对象的指针
- 如何成功地将函数对象(或lambda)传递给trackbar回调的第二个参数(void*)
- "std::function"的简单版本:函数对象的生存期?
- C++ 将函数对象作为左值和/或右值传递
- SFINAE 用于具有默认参数的函数对象
- 构造函数对象赋值是否泄漏内存
- 如何发送通过绑定到函数/方法创建的函数对象?
- std::for_each 与函数对象
- 将函数对象传递给 std::function
- 访问执行策略for_each函数对象中的迭代器
- 通过C++函数对象类访问参数