C++在虚拟方法中使用捕获 lambda
C++ using capturing lambda inside virtual method
我尝试使用以下C++代码,但是在使用g ++ 4.9.2和--std=c++11
进行编译时,它无法编译,并出现no matching function for call to ...
错误,以调用bar::s
内部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:
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
s 会使虚拟和非虚拟情况的编译失败(删除捕获的scale
仍然允许成功编译)。
无论如何,有没有一种简单且有效的方法来解决此问题?我想在模板类的虚拟和非虚拟方法中使用带有捕获(或类似的东西)的 lambda。我知道std::function
,但它是相当重量级的。而且无捕获的 lambda 比捕获的 lambda 强大得多,所以它们可能无法解决我的问题。
PS:如果你想知道z
应该做什么:它是Haskell中zipWith
后跟foldl
的组合(或者如果你不知道Haskell,有点像mapreduce有两个输入列表),可以使用foo
s用于大多数一元和二进制操作。在我的原始实现中,init
不是类型T
,而是用作一个额外的模板参数,为了简单起见,此处删除了该参数。
使用模板。
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,其状态可以随之而来,并且一切正常。
正如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 并忽略。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 虚拟决赛作为安全
- 可组合的lambda/std::函数与std::可选
- PowerPC ppc64le上的Gcc Woverloaded虚拟错误
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 如何在C++中获得"静态纯虚拟"功能?
- C++无法定义虚拟函数 OUTER 类和头文件
- 如何将lambda作为模板类的成员函数参数
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 在 lambda 捕获中声明的变量的类型推导
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 模板函数指针和lambda
- 两组使用lambda函数的大括号
- 与lambda一起使用虚拟继承在初始化列表中捕获此问题的GCC错误
- C++在虚拟方法中使用捕获 lambda
- 将纯虚拟接口替换为 lambda