为什么我不能将元素移动到不可复制的向量中?

Why can't I move an element into vector of non-copyables?

本文关键字:可复制 向量 不能 元素 移动 为什么      更新时间:2023-10-16

编译器告诉我我正在尝试访问已删除的函数(即lambda表达式的复制构造函数(。但是我看不到哪里。

std::vector<std::function<void()>> tasks;
std::packaged_task<int()> task{ [] { return 1; } };
tasks.emplace_back(
    [ t = std::move(task) ] () mutable { t(); });

(代码也在这里(

(我正在尝试找出为什么他们在https://www.slideshare.net/globallogicukraine/c11-multithreading-futures中使用shared_ptr<task>(。

在GCC和MSVC上,我会遇到相同的错误 - 我担心自己做错了什么...

error: use of deleted function 
'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'

为什么我不能将此std::function放在向量上?

来自cppreference:

f必须满足可呼叫和复制构造的要求

其中F是用于构建std::function的功能类型。但是,std::packaged_task不能复制构造。因此,在捕获列表中, t不是复制构造的,并且是lambda的非静态成员,使删除了lambda的隐式复制构造函数。

简短答案:lambdas和 std::packaged_task不是 std::function s。

长答案,您不能将std::packaged_task移至std::function

这是我提供的解决方案:

std::vector<std::packaged_task<int()>> tasks;
std::packaged_task<int()> task{ [] () mutable { return 1; } };
tasks.emplace_back( std::move(task) );

如果您实际上需要一个std ::功能,而不仅仅是任何可召唤的功能,则必须将lambda绑定到std::function

std::function的构造函数要求传递的函数对象为 CopyConstructible,但 std::packaged_task<F>不是(对于任何F(。std::function执行类型擦除,其中动态类型在静态类型中不可见。考虑例如:

int invoke(std::function<int()> f) { return f(); }
int main()
{
    std::packaged_task<int()> p{/*etc*/};
    auto l = [] { return 5; };
    std::function<int()> f( /* either p or l */ );
    std::cout << invoke(f) << 'n';
}

呼叫invoke需要复制f(按值通过(。但是,如果f是由l制成的,则可以复制,但如果它是由p制成的,则不能复制,这与f的静态类型无关。基本上有三种解决此问题的方法:

  • 禁止在编译时复制std::function
  • 允许在编译时复制std::function,但是如果包含的类型不可复制,请在运行时错误。
  • 允许在编译时复制std::function,并需要您放入其复制的任何功能对象。

方法#1对如何存储,通过和共享函数的方式非常限制,并且基本上禁止常见用例,而有利于不常见的情况使用不可拷贝的函数对象。

方法#2是有问题的,因为需要教育用户在某些情况下复制std::function可能会失败,并且在编写代码时会非常勤奋。同样,如果设计需要共享功能,则可能需要将其包裹在std::shared_ptr中。而且,如果需要复制并且可能是有状态的,那就变得更糟了。

无论您如何查看方法#3,都是标准化的方法。但是鉴于上述问题,也很容易辩护。

实际上,我编写了一个unique_function类模板,该模板使用方法1用于我当前的项目不需要。