C++ 在虚方法中使用捕获 lambda

标签 c++ c++11 lambda virtual-functions dynamic-dispatch

我尝试使用下面的 C++ 代码,但它无法编译,no matching function for call to ... error for the call to z inside bar::s 使用 g++ 4.9.2 和 --std=c++11 编译时:

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。如果 s 是虚拟的,我假设 z 是通过动态调度调用的,即使 z 不是虚拟的。该分析是否正确?

我不明白的是为什么 z 不能通过普通方法调用来调用——毕竟它不是虚拟的。我也不明白为什么删除模板参数并将所有 T 替换为 float 会使虚拟和非虚拟情况下的编译失败(删除捕获的 scale 仍然允许成功编译)。

无论如何,是否有一种简单且有点有效的方法来解决这个问题?我想在模板类的虚拟和非虚拟方法中使用带有捕获(或类似的东西)的 lambda .我知道 std::function,但它相当重量级。无捕获 lambda 的功能远不如带捕获的 lambda,因此它们可能无法解决我的问题。

PS:如果您想知道 z 应该做什么:它是 zipWith 后跟 foldl 的组合> 在 Haskell 中(或者有点像带有两个输入列表的 mapreduce,如果你不知道 Haskell)并且可以用于大多数使用 foo 的一元和二元操作。在我最初的实现中,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;
}

Live Demo

现在 z 通过其实际类型接受您的 lambda,它的状态可以被携带,并且一切正常。

关于C++ 在虚方法中使用捕获 lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55093841/

相关文章:

java 8 Stream List<Map> 如何在 groupingBy 后转换 Map

c++ - reverse_iterator 适配器

c++ - std::alignas 如何优化程序的性能?

c++ - 为什么 C++11 中没有 `static_if`

java - Lambda 表达式如何转换成 Java 字节码

java - 为什么java lambda会跳转过滤器

c++ - 在 glm::rotate 中使用 "custom"sin 和 cos

c++ - void_t 和带有 decltype : are they completely interchangeable? 的尾随返回类型

c++ - 在 Google Mock 上调用重载函数

c++ - 程序是否不使用无法绑定(bind)到引用参数的默认参数,是否合法?