我在C ++中有一个与函数对象或仿函数有关的问题。我知道在很多情况下函数对象可以替换函数指针,例如STL中的一些算法。但是,如果函数的参数是函数指针,我不知道如何调用函数对象。例如:
class ClassA
{
public:
void operator()(std::string &name)
{
std::cout<<"Class A"<<std::endl;
}
};
class ClassB
{
public:
void operator()(std::string &name)
{
std::cout<<"Class B"<<std::endl;
}
};
void testClass(void(*fun)(std::string&), std::string &name )
{
(*fun)(name);
}
void funClass(std::string &str)
{
std::cout<<"hello C "<<str<<std::endl;
}
如果我只使用函数指针,那么调用testClass
函数非常容易:
std::string str= "hello.txt";
testClass(funClass,str);
但是,如果我想使用函数对象,我不知道如何调用testClass。
ClassA obj;
testClass(obj.operator()(str),str); // compilation errors
答案 0 :(得分:2)
A&#34;仿函数&#34; class不是函数指针。但是std::function
需要构造函数,它将隐式地从函子或函数指针创建一个。
如果允许修改testClass,那么理想情况下要使它更通用:
void testClass( std::function< void( std::string & ) > func, std::string & name )
{
func( name );
}
然后你可以打电话
testClass( ClassA(), name );
如果不修改字符串,则应该通过const引用来获取此参数。
您可以为您的课程编写代理功能,这样就可以转发他们,但我不认为这是您真正想要的,因为您必须为每个代表编写一个:< / p>
否则你可以编写像这样的模板化函数
template< typename T >
void callT( std::string & name )
{
T t;
t( name );
}
testClass( callT< ClassA > );
如果您正在使用C++11
,则可以在实例中传入lambda函数,该函数将转换为函数指针。 (感谢Steve Jessop告诉我这个。)
在这种情况下,这是一个解决方法,因为您必须为要使用它的每个仿函数类型输出lambda函数。 (所以即使该功能存在,模板也可能更好)。
用你要做的lambda来做
testClass( []( std::string & str ){ ClassA a; a(str); }, name );
请注意,要使lambda能够转换为函数,它不能使用验证码,但如果您需要额外的数据成员,则无法进行模板化版本。
答案 1 :(得分:1)
类实例通常不是函数指针,而是一个不捕获任何内容的lambda类实例,它隐式转换为函数指针:
auto const f = []( std::string& s ) { classA()( s ); };
testClass( f, "blah" );
或者使用普通的命名函数。
标准库算法可以使用带有operator()
的原始函数指针或类实例,因为该类型是模板化的。
更一般的问题,如何将可能有状态的仿函数对象转换为原始函数指针,例如对于C回调,更有意思。
遗憾的是,标准库缺乏对此的支持。
答案 2 :(得分:0)
将testClass
函数重写为函数模板。
template<typename T>
void testClass(T fun, std::string& name) {
fun(name);
}
然后它可以接受函数指针以及函数对象的实例。
这比触发std::function
机器更简单。
C ++函数指针具有这种灵活性,您不必强制编写*fun()
来调用指向函数。你可以写fun()
。
顺便说一下,你也可以传递函数的引用。
答案 3 :(得分:0)
您还可以使用线程局部堆栈的函数对象。这就像是
template<typename Sig> struct horrible_hackery;
template<typename Ret, typename... Args> struct horrible_hackery {
static thread_local std::stack<std::function<Ret(Args...)>> functions;
static Ret trampoline_callback(Args... args) {
return functions.top()(args...);
}
template<typename F, typename Around> static void execute_around_fptr(F f, Around a) {
functions.push(f);
a(&trampoline_callback);
functions.pop();
}
};
现在你可以使用
ClassA obj;
horrible_hackery<void(std::string&)>::execute_around(obj, [&](void(*fptr)(std::string&)) {
return testclass(fptr, str);
});
是这个版本的C ++ 03版本,这是你不想考虑的更糟糕的可怕hackery。
如果你真的非常渴望这个功能,并且你不能接受上面纯粹的基于堆栈的解决方案,你可以为你选择的平台下载一个JIT编译器,如LLVM,然后JIT thunk for你自己。这非常极端。
然而,这些限制和通常可怕的hackery导致以下结论: