成员函数指针模板替换失败



我有以下代码:

class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
template<class T, typename ... ARGS>
std::function<void()> * invoke(T *t, void(T::* fn)(ARGS...), ARGS... args) {
//Create a new std::function on the heap to be executed later
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}
int main(void) {
A myA;
B myB(5, 6, 7);
std::function<void()> *fn = invoke(&myA, &A::foo, myB);
}

其目的是能够从堆上的成员函数指针(和对象指针(创建通用std::function,以便稍后执行。

我的问题是,编译器似乎无法找出如何正确扩展调用模板,我得到了以下错误:

template argument deduction/substitution failed:
inconsistent parameter pack deduction with 'const B&' and 'B'

我希望invoke((函数的语义保持不变(即对象*、成员函数*、参数…(

有没有一种方法可以维护这些语义,同时仍然允许编译器计算出正确的模板推导?

谢谢!

编辑

如果我做以下事情,我可以让它工作:

template<class T, typename F, typename ... ARGS>
std::function<void()> * invoke(T *t, F const &fn, ARGS... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}

然而,这并不能完全满足我的需要,因为论点是两份而不是一份。我希望在创建堆上分配的新std::函数时只出现一个副本。参数仍应通过引用传递,以计算成员函数的签名。

您的invoke函数已被称为std::bind,如果您想确保foo的参数不被复制,请将bindcref组合:

#include <functional>
struct B {
int a() const { return 1; }
};
struct A {
void foo(const B& b) {
int a = b.a();
}
};
int main(void) {
A myA;
B myB;
auto* fn = new std::function<void()>(std::bind(&A::foo, &myA, std::cref(myB)));
}

首先,在处理参数包时,您应该真正使用std::forward。现在,您正在将所有参数作为值。

其次,由于void(T::* fn)(ARGS...)ARGS... args之间的类型推导冲突,导致编译失败。编译器会混淆是从args还是从函数中获取类型。例如,A::foo接受一个const B&,但您也给它一个值类型B,这会导致冲突。因此,您实际上需要两个单独的参数包来避免这种情况。

#include <functional>
#include <utility>
// you didn't provide a definition of B, so this is what I had to come up with
struct B {
int x;
int y;
int z;
int a() const {
return x;
}
};
class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
// you don't actually need to create a function on the heap
template<class T, typename ... FARGS, typename ...ARGS>
std::function<void()> invoke(T *t, void(T::* fn)(FARGS...), ARGS &&... args) {    
return [&, t]() { (t->*fn)( std::forward<ARGS>(args)... ); };
}
int main(void) {
A myA;
B myB{5, 6, 7};
std::function<void()> fn = invoke(&myA, &A::foo, myB);
}

或者,您也可以使用std::bind,它正是您想要实现的:

std::function<void()> fn = std::bind(&A::foo, &myA, myB);

invoke的参数和成员函数的参数不相同。一个获得常量引用,另一个没有。这需要反映在类型中。

template<class T, 
typename ... ARGS, 
typename ... ARGS2> // <---- !!
std::function<void()> * invoke(T *t, 
void(T::* fn)(ARGS2...) // <---- !! 
ARGS&&... args) { // you do want perfect forwarding

我根据每个人的回答和评论找到了一个适合我的解决方案。

在我的应用程序中,我需要确保为被调用的函数创建一个且只有一个参数副本,因为该函数将在不同的时间在不同的线程中执行,并且原始数据可能不可用(例如堆栈上的临时变量(。按照建议,我修改了代码,以便在模板函数中使用完美的转发。这有助于显著减少不必要的副本,然而,我仍然在调用函数的lambda中获得了一个额外的副本。事实证明,我错误地编写了数据类型的move构造函数,因此,当lambda创建数据的临时副本时,它必须复制两次。

以下是我的工作代码片段(示例数据类型为B(:

class B {
public:
B() : _a(1), _b(2), _c(3) {
}
B(int a, int b, int c) : _a(a), _b(b), _c(c) {
}
//Copy Constructor
B(const B &v) : _a(v._a), _b(v._b), _c(v._c) {
copyCount++;
}
//Move Constructor
B(const B&& rhs): _a(rhs._a), _b(rhs._b), _c(rhs._c)
{
}
B& operator=(const B &v) {
this->_a = v._a;
this->_b = v._b;
this->_c = v._c;
copyCount++;
return *this;
}
~B() {
}
inline int a() const { return _a; }
static int copyCount;
private:
int _a;
int _b;
int _c;
};
template<class T, typename ... ARGSF, typename ... ARGS>
inline static void invoke(T *t, void(T::*fn)(ARGSF...), ARGS&&... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
//Queue in the parent thread
if (!t->_parentThread->queueInvokable(f)) delete f;
}
template<class T>
inline ConnectionHandle connect(T* t, void(T::* fn)(ARGS...)) {
const auto lambda = [=](ARGS&&... args) { T::invoke(t, fn, args...); };
return connect(lambda);
}

我在这里还展示了另一个函数connect,它保存了一个lambda,以便稍后调用,然后再调用invoke函数。

通过所有这些,只要a( 成员函数(fn(的参数属于引用类型b( 该数据类型有一个有效的移动构造函数

最新更新