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

7
编译器告诉我正在尝试访问已删除的函数(即 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放到向量中?



1
如何将不可复制的std :: function存储到容器中? - cpplearner
@cpplearner 谢谢,所以这一切归结于 function 决定了是否可拷贝构造? - xtofl
3个回答

4

来自cppreference

F必须符合Callable和CopyConstructible的要求

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


我理解不可复制构造函数的概念,但是...但是...我并不想使用复制构造函数。为什么emplace_back([t]{})需要一个复制构造函数呢? - xtofl
啊!这里调用的是function(F)构造函数,因为我正在使用emplace一个lambda表达式!我明白了。谢谢。 - xtofl

2
简短回答:Lambda表达式和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 中。


0

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对函数的存储、传递和共享方式非常限制,并且基本上禁止了常见用例,而赞成使用不可复制的函数对象的不太常见的情况。

方法二存在问题,因为用户需要了解复制 std::function 可能会在某些情况下失败,并在编写代码时非常小心。此外,如果设计需要共享函数,则可能需要将它们包装在 std::shared_ptr 中。如果需要复制并且可能具有状态,则情况会变得更糟。

无论您如何看待方法三,它都是被标准化的方法。但考虑到上述问题,它也很容易被捍卫。

事实上,我为我的当前项目编写了一个 unique_function 类模板,使用方法一,因为对我们来说存储不可复制的异步任务对象的用例非常普遍,而复制或共享这样的任务并不是必要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接