在 lambda 中使用 std::bind 和 std::unique_ptr

use std::bind in lambda with std::unique_ptr

本文关键字:std unique ptr bind lambda      更新时间:2023-10-16
// By const l-value reference
auto func2 = std::bind([](const std::unique_ptr< std::vector<int> >& pw) // fine
{
std::cout << "size of vector2: " << pw->size() << std::endl;
}, std::make_unique<std::vector<int>>(22, 1));
//By non-const l-value reference
auto func3 = std::bind([](std::unique_ptr< std::vector<int> >& pw) // fine
{
std::cout << "size of vector3: " << pw->size() << std::endl;
}, std::make_unique<std::vector<int>>(22, 1));
// By Value
auto func4 = std::bind([](std::unique_ptr< std::vector<int> > pw) // error
{
std::cout << "size of vector4: " << pw->size() << std::endl;
}, std::make_unique<std::vector<int>>(22, 1));
func4(); // without this line, compilation is fine. The VS generates error for the calling of the bind object.
// By r-value reference
auto func5 = std::bind([](std::unique_ptr< std::vector<int> >&& pw) // error
{
std::cout << "size of vector5: " << pw->size() << std::endl;
}, std::make_unique<std::vector<int>>(22, 1));
func5(); // without this line, compilation is fine.

为什么 func4 和 func5 编译失败?

func4会产生错误,因为 lambda 的参数是按值传递的。但std::unique_ptr是不可复制的。

func5更复杂,我们可以从文档中读取 std::bind:

给定从先前对绑定的调用中获得的对象 g,当它在函数调用表达式g(u1, u2, ... uM)中调用时,会发生对存储对象的调用,就像通过std::invoke(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN))一样,其中fd是绑定参数的值和类型的值std::decay_t<F>v1v2, ...,vN按以下规定确定。

普通存储参数arg作为 lvalue 参数传递给可调用对象:上面 std::invoke 调用中vn的参数只是 arg,对应的类型VnT cv &,其中cvg的 CV 资格相同。

因此,即使std::make_unique<std::vector<int>>(22, 1)是 r 值,也会为 lambda 提供 l 值,这与预期的 r 值不兼容。
这也可以解释为什么func3工作正常。

bind返回一个可以多次调用的函数对象。

它接受其参数并将其存储在元组(或等效元组)中。 然后,它用其余参数调用第一个参数。 这与 C++17 中的std::invoke类似。

对于这两种失败的情况,您都无法多次调用 lambda。 因此,当您调用它一次时,您会收到错误,因为bind 假定您希望能够再次调用它。 做任何其他事情都是疯狂的,因为它无法知道你永远不会在它的operator()的背景下再次调用它。

从逻辑上讲,这些调用应该会失败。 该标准还要求它们失败,因为在这种情况下,标准的行为是合乎逻辑的。


auto funcA =
[pw=std::make_unique<std::vector<int>>(22,1)]
{
std::cout << "size of vector2: " << pw->size() << std::endl;
};
auto funcB =
[pw=std::make_unique<std::vector<int>>(22,1)]() mutable
{
std::cout << "size of vector2: " << pw->size() << std::endl;
};

下面是两种不同的 lambda,它们大致执行您的代码所做的事情。 我们只是捕获,而不是绑定和传递。

funcA我们有一个const unique_ptr,在funcB我们有一个非constunique_ptr。 在第二个中,我们可以移出唯一的 ptr;首先,我们不能。

std::bind是在 C++ 中存在的 lambda 之前编写的,很少有比使用 lambda 更好的主意。 lambda 中的缺陷大多在 C++14 之前已经消除,并且在极少数情况下使用bind而不是 lambda 是一个好主意。

std::bind生成神秘的错误消息,并且在许多极端情况下具有神秘的行为,例如将bind的结果传递给另一个bind