提问人:Damian 提问时间:10/26/2023 最后编辑:Damian 更新时间:11/1/2023 访问量:151
创建shared_ptr时不需要的复制构造函数调用
Unwanted copy constructor call when creating a shared_ptr
问:
在进行一些编码练习时,我在尝试将可选的所有权传递给 :std::unique_ptr
std::shared_ptr
/usr/include/c++/11/ext/new_allocator.h:162: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]’
162 | { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/11/memory:76,
from main.cpp:2:
/usr/include/c++/11/bits/unique_ptr.h:468:7: note: declared here
468 | unique_ptr(const unique_ptr&) = delete;
| ^~~~~~~~~~
这是导致我错误的特定片段(为无意义的代码道歉):
#include <optional>
#include <memory>
class test_class
{
public:
struct options
{
options();
std::optional<std::unique_ptr<uint8_t>> data;
};
test_class(const test_class::options &options = test_class::options());
private:
std::shared_ptr<std::unique_ptr<uint8_t>> _data;
};
test_class::test_class(
const test_class::options &options)
{
if (options.data.has_value())
{
_data = std::make_shared<std::unique_ptr<uint8_t>>(std::move(options.data.value()));
}
}
我以为我会通过使用调用绕过任何复制构造函数,但似乎我可能无意中调用了它。std::move
此外,我运行了这个类似的代码片段,但没有错误:
test_class::test_class(
const test_class::options &options)
{
std::optional<std::unique_ptr<uint8_t>> data;
if (data.has_value())
{
_data = std::make_shared<std::unique_ptr<uint8_t>>(std::move(data.value()));
}
}
谁能解释为什么会这样?为什么第一个片段是错误的?我是否误解了一些 C++ 基础知识?为什么第二个代码段不会也失败?
谢谢!
答:
如上面的评论所述,此行为是构造函数的参数的结果。使用 的用途旨在转换为 .但是,由于 const 限定符,它阻止了 move 构造函数的使用。因此,代码尝试从此强制转换值构造新unique_ptr,但回退到使用复制构造函数而不是移动构造函数。test_class::options
const
std::move
options.data.value
const std::unique_ptr<uint8_t>&&
我更正如下:
#include <optional>
#include <memory>
class test_class
{
public:
struct options
{
options();
std::optional<std::unique_ptr<uint8_t>> data;
};
test_class(test_class::options &&options = = dst_file::options());
private:
std::shared_ptr<std::unique_ptr<uint8_t>> _data;
};
test_class::test_class(
const test_class::options &options)
{
}
test_class::test_class(
test_class::options &&options)
{
if (options.data.has_value())
{
_data = std::make_shared<std::unique_ptr<uint8_t>>(std::move(options.data.value()));
}
}
由于现在是“右值引用”(由 表示),因此默认情况下它将调用移动构造函数。test_class::options
&&
评论
std::move
options.data.value
const std::unique_ptr<uint8_t>&&
unique_ptr
const
复杂的嵌套指针在某种程度上掩盖了潜在的问题。所有权在这里并不是真正的问题,从unique_ptr创建shared_ptr也没有问题。您得到的错误仅与unique_ptr有关。在这里,提到的“模糊”变得明显,因为错误很难理解。但是,如果我删除所有绒毛,例如 std:: 和模板参数,它归结为
使用已删除的函数 unique_ptr::unique_ptr(const unique_ptr &),
这是复制构造函数。删除unique_ptr的复制构造函数,因为无法复制unique_ptr;毕竟,它是独一无二的。
但是,为什么复制构造函数是通过重载分辨率来选择的呢?因为声明的 move 构造函数是从不是 const 的右值引用移动的。const rvalue 引用根本不匹配。然后,回退是尝试使用复制构造函数,因为 const 引用可以绑定到常量右值引用;但正如我们所说,删除这个是有充分理由的。
下面是演示简单类 S 工作原理的代码。你的构造函数案例是用 来演示的,但最简单的演示实际上是 中的最后一行: 你根本无法构造一个 from a const rref。an 可能是不同类的成员,并且构造是该类初始化的一部分,这种情况对问题并不重要。test_class
main()
S
S
#include <utility> // std::move
using namespace std;
// this takes on the role of unique_ptr in your example
struct S
{
S() = default;
// [1] enable the copy constructor ...
//S(const S&) = default;
// [2] ... or the move constructor from rref to const;
//S(const S&&) {}
// [3] OR remove the move constructor; that will un-delete the copy ctor
S(S&&) = default;
};
class test_class
{
public:
S _s;
// Does not work because no constructor matches. In the order of
// resolution attempts:
// 1. The best match, a constructor taking a const rref argument, is not declared, see [2]
// 2. The declared regular move constructor [3] taking a non-const rref argument does not match;
// 3. The regular copy constructor taking a const ref argument
// is implicitly deleted because we declared a move constructor [3].
// We could explicitly define it, [1]
// test_class(const S &sArg): _s{move(sArg)} {}
};
int main()
{
const S cs;
// move actually works like a charm but produces an rref to const.
const S &&s_rref{move(cs)};
// construction from a const rref is not defined.
// S s{move(cs)};
}
评论
std::make_shared<std::unique_ptr<uint8_t>>
会创建一个shared_ptr unique_ptr。完全没有意义......const
const
int
unique_ptr