尝试将lambda函数放在队列中时出现一般分配器错误(可能是与unique_ptr有关的错误)

Generic allocator error when trying to place lambda function on queue (probably something related to unique_ptr)

本文关键字:错误 unique ptr 分配器 函数 lambda 队列      更新时间:2023-10-16

在尝试将lambda函数放入队列时,我遇到了那些通用的C++分配器错误。我想这可能与我的类Buffer中的unique_ptr有关。

我知道,当我添加unique_ptr时,我会使其无法复制类,因为unique_ptr删除了它的复制构造函数。然而,我可以将它复制到lambda函数的内部(不知道如何(。这个问题似乎只有在我试图复制lamdba函数本身时才会出现。

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <queue>
typedef std::function<void()> Task;
std::queue<Task> tasks;
template <class T>
class Buffer final
{
public:
static Buffer fromOwned(T buffer, size_t size)
{
return Buffer(std::make_unique<T>(buffer), size);
}
Buffer(std::unique_ptr<T> buffer, size_t size)
{
_buffer = std::move(buffer);
_size = size;
}
size_t size() {
return _size;
}
private:
std::unique_ptr<T> _buffer;
size_t _size = 0;
};
int main()
{
auto b = Buffer<uint8_t*>::fromOwned(new uint8_t[1], 1);
auto f = [b]() mutable {
b.size();
};
tasks.emplace(f);
}

错误:

<source>: In function 'int main()':
<source>:34:12: error: use of deleted function 'Buffer<unsigned char*>::Buffer(const Buffer<unsigned char*>&)'
34 |   auto f = [b]() mutable {
|            ^~~~~~~~~~~~~~~
35 |       b.size();
|       ~~~~~~~~~
36 |   };
|   ~         
<source>:10:11: note: 'Buffer<unsigned char*>::Buffer(const Buffer<unsigned char*>&)' is implicitly deleted because the default definition would be ill-formed:
10 |     class Buffer final
|           ^~~~~~
<source>:10:11: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = unsigned char*; _Dp = std::default_delete<unsigned char*>]'
In file included from /opt/compiler-explorer/gcc-trunk-20200709/include/c++/11.0.0/memory:83,
from <source>:3:
/opt/compiler-explorer/gcc-trunk-20200709/include/c++/11.0.0/bits/unique_ptr.h:468:7: note: declared here
468 |       unique_ptr(const unique_ptr&) = delete;
|       ^~~~~~~~~~
Compiler returned: 1

我尝试读取new_allocator.h文件,但它没有告诉我任何关于错误的信息,这是非常普遍的。为什么会发生这种情况?有人有阅读这些超通用错误的技巧吗?

我试着在课堂上添加复印作业:

Buffer(Buffer &buffer) : _size(buffer._size),
{
_buffer = std::move(buffer._buffer);
}

Buffer &operator=(Buffer & buffer) 
{
_size = buffer._size;
_buffer = std::move(buffer._buffer);
}

希望它不会依赖于unique_ptr的复制构造函数,但它没有改变任何东西。

您需要在捕获中使用move,假设您使用的是C++14或更早版本:

[b = std::move(b)]() mutable {
b.size();
}

现在来看细节。。。首先,当编译器试图构建lambda捕获时,会出现错误,当lambda被复制时,NOT。由于Buffer的复制构造函数已被删除(因为unique_ptr成员(,因此无法复制Buffer对象。

您可以通过实现复制构造函数来解决这个问题,但是您的实现并不完善,因为";复制";窃取对象的所有权。您实际上应该复制缓冲区:

Buffer( const Buffer& other )
: _size( other._size )
, _buffer( std::make_unique< T >( *other._buffer ) ) // <- rely on the copy constructor of `T`
{
}
Buffer& operator=( Buffer other )
{
_size = other._size;
_buffer = std::move( other._buffer );
return *this; // <- you forgot this.
}

但是,您可以保留默认的移动语义:

Buffer( Buffer&& other ) = default;

有了这些定义,您将能够按价值进行捕获。