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

时间:2017-06-06 07:32:44

标签: c++ vector move-semantics

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

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

code is also here

(我试图找出他们在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置于向量上?

3 个答案:

答案 0 :(得分:4)

来自cppreference

  

F必须符合Callable和CopyConstructible

的要求

其中F是用于构造std::function的函数类型。但是,std::packaged_tasknot copy constructible。因此,在捕获列表中,t不是可复制构造的,并且是lambda的非静态成员,使得lambda的隐式复制构造函数被删除。

答案 1 :(得分:2)

简短回答:Lambdas和std::packaged_task不是std::function

答案很长,您无法将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 :: function,而不仅仅是任何可调用的,你必须将lambda绑定到std::function

答案 2 :(得分:0)

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

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,则不可复制,这与{{1}的静态类型无关}}。这个问题基本上有三种方法:

  • 禁止在编译时复制f
  • 允许在编译时复制std::function,但如果包含的类型不可复制,则抛出运行时错误。
  • 允许在编译时复制std::function,并要求您放入其中的任何函数对象都可以复制。

方法#1对如何存储,传递和共享函数有非常严格的限制,并且基本上禁止使用不可复制的函数对象的常见用例。

方法#2是有问题的,因为用户需要接受教育,在某些情况下复制std::function可能会失败并在编写代码时使用尽职调查。此外,如果设计需要共享功能,则可能需要将其包装在std::function中。如果他们需要被复制并且可能是有状态的,那就更糟了。

无论你如何看待方法#3,它都是标准化的方法。但鉴于上述问题,也很容易捍卫。

事实上,我编写了一个std::shared_ptr类模板,它使用方法#1进行当前项目,因为对我们来说,存储不可复制的异步任务对象的用例很常见,并且复制或者分享这样的任务是没有必要的。