我尝试使用以下C ++代码,但使用g ++ 4.9.2和{{编译时,在no matching function for call to ...
内对z
的调用bar::s
时,编译失败,并出现--std=c++11
错误1}}:
template <class T>
class foo {
public:
virtual void s( T scale , const foo<T>& rhs ) {
}
};
template <class T>
class bar : public foo<T> {
public:
T z( T init , T (*f)( T a , T& x , const T& y ) , const foo<T>& rhs ) {
}
void s( T scale , const foo<T>& rhs ) {
this->z( ((T)0) , [=]( T a, T& l, const T& r ) {return ((l+=scale*r)?((T)0):((T)0));} , rhs );
}
};
int main () {
bar<float>* a = new bar<float>();
foo<float>* b = new foo<float>();
return 0;
}
如果我删除方法定义前面的virtual
,也要从lambda中删除捕获的scale
,则代码会编译。如果正确理解了该问题,则其工作方式如下:如果该方法不是虚拟的,则通过与其关联类型的operator ()
使用带有捕获的lambda。如果该方法是虚拟的,则不能使用该运算符,因为它不能通过动态分派(仅通过常规方法调用)来调用。但是lambda也不能转换为函数指针,因为该转换仅适用于无捕获的lambda。而且我假设即使z
不是虚拟的,如果s
是虚拟的,也会通过动态调度调用z
。这种分析正确吗?
我不明白的是为什么不能通过常规方法调用来调用z
-毕竟它不是虚拟的。我还没有得到的是为什么删除模板参数并用T
替换所有float
会导致虚拟和非虚拟情况下的编译失败(删除捕获的{{1 }}仍允许成功编译。
无论如何,是否有一种简单且有效的方法来解决此问题?我想在模板类的虚拟和非虚拟方法中使用带有捕获(或类似内容)的lambda。 。我知道scale
,但是它很重。而且,无捕获Lambda的功能要比具有捕获的Lambda强大得多,因此它们可能无法解决我的问题。
PS:如果您想知道std::function
应该做什么:它是Haskell中z
后跟zipWith
的组合(或有点像mapreduce与两个输入列表(如果您不知道Haskell,则可以使用foldl
进行大多数一元和二进制运算)。在我最初的实现中,foo
的类型不是init
,而是用作附加模板参数的类型,为简单起见,此处将其删除。
答案 0 :(得分:2)
使用模板。
virtual
实际上与您的问题无关。问题在于原始函数指针不能携带任何状态,因此带有捕获的lambda不能转换为函数指针。
您应该使z
成为可以接受任何类型作为其第二个参数的模板,而不是原始的函数指针:
template <class T>
class foo {
public:
virtual void s( T scale , const foo<T>& rhs ) {
}
};
template <class T>
class bar : public foo<T> {
public:
template <typename F>
T z( T init , F&& f, const foo<T>& rhs ) {
f(/*some args*/);
}
void s( T scale , const foo<T>& rhs ) {
this->z( ((T)0) , [=]( T a, T& l, const T& r ) {return ((l+=scale*r)?((T)0):((T)0));} , rhs );
}
};
int main () {
bar<float>* a = new bar<float>();
foo<float>* b = new foo<float>();
return 0;
}
现在z
按其实际类型接受您的lambda,可以携带其状态,并且一切正常。
答案 1 :(得分:0)
正如雷蒙德·陈(Raymond Chen)所指出的那样,虚拟性确实是一条红鲱鱼。
暂时,我决定采用这种丑陋的解决方法,但由于它不是真正的解决方案,所以我不接受我的回答。
template <class T>
class foo {
public:
virtual void s( T scale , const foo<T>& rhs ) {
}
};
template <class T>
class bar : public foo<T> {
public:
T z( T init , T (*f)( T a , T& x , const T& y ) , const foo<T>& rhs ) {
}
template <class U>
U z2( U init , T capture_0 , T capture_1 , U (*f)( T capture_0 , T capture_1 , U a , T& x , const T& y ) , const foo<T>& rhs ) {
return init;
}
void s( T scale , const foo<T>& rhs ) {
this->z2<T>( ((T)0) , scale , ((T)0) , []( T c0 , T c1 , T a, T& l, const T& r ) {return ((l+=c0*r)?((T)0):((T)0));} , rhs );
//this->z( ((T)0) , [=]( T a, T& l, const T& r ) {return ((l+=scale*r)?((T)0):((T)0));} , rhs );
}
};
int main () {
bar<float>* a = new bar<float>();
foo<float>* b = new foo<float>();
a->s(0,*b);
return 0;
}
这可以编译并允许我使用z2
,最多可以使用2个正确类型的“伪捕获”,带有或不带有虚拟性,甚至可以使用我在问题中遗漏的其他模板参数。不需要的捕获可以简单地设置为0并忽略。