多态性在 lambda 函数C++不起作用

Polymorphism doesn't work in C++ lambda function

本文关键字:C++ 不起作用 函数 lambda 多态性      更新时间:2023-10-16

我有这样的方法:

void syncOperation(ProgressCallback& progressCallback);

其中ProgressCallback是:

class ProgressCallback
{
public:
virtual void onProgress(std::size_t currValue, std::size_t maxValue) {}
virtual void onDone() {}
};

我想让它异步,所以我执行以下操作:

void asyncOperation(ProgressCallback& progressCallback)
{
auto impl = [this](ProgressCallback& progressCallback_)
{
syncOperation(progressCallback_);
};
jobsPool.addJob(std::bind(impl, progressCallback));
}

但是在第两种情况(asyncOperation(ProgressCallback&))中progressCallback的行为不是多态的,它总是调用基类的方法,这绝对不是我所期望的。所以我的问题是:1)为什么会发生这种情况,2)如何解决它(是的,我知道我可以停止使用lambdas,但也许有一些传统的解决方法)?

你显示的代码没有多态的空间,但让我们假设asyncOperation传递了一个对派生自ProgressCallback的东西的引用:

当您将引用传递给bind时,您正在创建副本。 绑定返回的对象根据它所包含的有关它的类型信息存储值。 它只知道它有对ProgressCallback的引用,所以这就是它存储的值。 这会导致切片副本。

您需要将值包装在reference_wrapper中:

jobsPool.addJob(bind(impl, ref(progressCallback));

或者你可以使用lambda(更好):

jobsPool.addJob([impl,&progressCallback](){ impl(progressCallback); });

或者,您可以创建impl,以便它自己完成所有操作。

您现在正在执行的操作相对等效于:

jobsPool.addJob([impl,=progressCallback]() mutable { impl(progressCallback); });

你不需要定义一个lambda,然后使用绑定,或者使用两个lambda,你可以一次性完成这一切:

void asyncOperation(ProgressCallback& progressCallback)
{
jobsPool.addJob([&] { syncOperation(progressCallback); });
}

如前所述,在原始代码中,由于正在发生的切片副本,您被咬了。由于模板参数推导的工作原理,延迟函数调用(如std::bind)需要通过引用包装器传递引用参数。这种相当奇怪的行为是另一个很好的理由,一旦您可以访问 lambda,就根本不使用bind。但请注意,此行为也适用于std::thread

请注意,存储通过引用捕获某些内容的 lambda 是一件非常危险的事情。您可以使用 progressCallback 对象调用asyncOperation,该对象随后处于活动状态,并且该对象可能在执行 lambda 时被破坏,从而导致未定义的行为。