std::thread 和许多其他库不接收函数指针。他们接收函子。
functor 是所有可以用() 操作符调用的东西。所以,函数指针就是函子:
void (*pf)();
// we can do it
pf();
我们也可以调用重载operator ()的对象。
struct Functor
{
void operator ()();
};
Funtor f;
// we also can do it
f();
成员函数指针不是函子。 (你不能mpf(this);。this->*mpf(); 是正确的。)但是,std::thread 的构造函数执行 bind。
例如:
#include <iostream>
#include <functional> // for std::bind
template <typename F>
void call_functor(F f)
{
f();
}
void foo() { std::cout << "foo\n"; }
struct bar { void operator ()() { std::cout << "bar\n"; } };
void WithParam(int i) { std::cout << "param : " << i << "\n"; }
class Cls
{
public:
void member() { std::cout << "Cls::member\n"; }
};
int main()
{
// we can do these, because both foo and b are functor.
bar b;
call_functor(foo);
call_functor(b);
// bind `123`
call_functor(std::bind(WithParam, 123));
// bind `c`
Cls c;
call_functor(std::bind(&Cls::member, &c /* this pointer */));
}
看这个:call_functor(std::bind(WithParam, 123));。虽然WithParam不能用f()调用,因为它有一个参数,但我们可以通过将WithParam的参数绑定到123来使用WithParam。
std::bind不仅可以绑定参数,还可以绑定this指针和成员函数指针。所以我们也可以这样做:call_functor(std::bind(&Cls::member, &c /* this pointer */));。
我们无法知道所有仿函数的类型。例如:void (*)()、struct bar 或 std::bind...
所以,要接收函子作为参数,我们应该使用模板。比如看std::thread的构造函数:
template<typename _Callable, typename... _Args>
explicit
thread(_Callable&& __f, // <-- here
...
在你的情况下,你应该这样做:
class x
{
public:
template <typename Functor>
void executeQuery(std::string query, Functor f)
{
...
int result = f(cpuInfo, argc, argv, azColName);
...
}
};
...
myclass()
{
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
}
你介意使用模板吗(也许你想隐藏executeQuery的实现)?然后,还有另一个解决方案,std::function<>。它是函子的多态包装器。它比函子慢一点,但如果你介意使用模板,它可能是一个很好的解决方案。
class x
{
public:
void executeQuery(std::string query,
const std::function<int ()(void*, int, char**, char**)> &f);
};
用法几乎等于函子。 (也许你需要显式地转换成std::function<>)事实上,std::function 也是函子! (可以用()操作符调用..)
编辑: 嗯.. 我刚刚注意到您正在像这样使用f:
int rc = sqlite3_exec(db, query.c_str(), &f, 0, &errMessage);
sqlite3_exec 的第三个参数可能是函数指针吧?它会变得复杂..
根据here,sqlite3_exec 的第四个参数将作为第一个参数传递给f。然后,我们可以这样写代码:
// header file of `class x`
class x
{
private:
static void ExecuteQuery_Callback(void *param, int argc, char** argv, char** azColName);
// use std::function<>
typedef std::function<int (void*, int, char**, char**)> ExecQueryFunctor;
void executeQuery_impl(std::string query, const ExecQueryFunctor &f);
public:
// wrapper
template <typename F> void executeQuery(std::string query, F f)
{
executeQuery_impl(query, ExecQueryFunctor(f));
}
};
// .cpp file of `class x`
void x::executeQuery_impl(std::string query, const x::ExecQueryFunctor &f)
{
int rc = sqlite3_exec(db, query.c_str(), ExecuteQuery_Callback, &f, &errMessage);
...
}
void x::ExecuteQuery_Callback(void *param, int argc, char** argv, char** azColName)
{
const ExecQueryFunctor *pfunctor = (const ExecQueryFunctor *)param;
(*pfunctor)(NULL, argc, argv, azColName);
}
edit2:嗯,我明白了问题所在。一方面,如果你这样做,效果会很好:
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
改成:
using namespace std::placeholders;
...
xObject->executeQuery("test", std::bind(&myclass::myfunction, this, _1, _2, _3, _4));
_1、_2、_3、_4 是占位符。 (如果你想了解占位符,请搜索 google..)
在这种情况下,我认为不需要占位符,但是如果没有占位符,则会发生错误...
edit3:我们应该使用占位符的原因:Why are placeholders required in std::bind in this case?