无法将带有 NULL 删除器的 std::unique_ptr 移动到 std::shared_ptr?
Cannot move std::unique_ptr with NULL deleter to std::shared_ptr?
我想将 NULL std::unique_ptr 移动到 std::shared_ptr,像这样:
std::unique_ptr<float> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
据我所知,这样做应该是合法的,并且在 Visual Studio 2015 和 GCC 中 运行 没问题。
但是,我不能对具有删除器声明的 std::unique_ptr 执行相同的操作,如下所示:
std::unique_ptr<float,void(*)(float*)> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
上面的代码不会在 visual studio 中编译,并且会触发静态断言失败 "error C2338: unique_ptr constructed with null deleter pointer"。
我可以改用 std::function 删除器,在这种情况下可以避免静态断言失败:
std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
在这种情况下,代码可以正常编译,但是一旦 std::shared_ptr 副本 test2被摧毁。
为什么后两种情况这么有问题?
奇怪的是,如果我将 test2 的类型从 std::shared_ptr 更改为 std::unique_ptr,第二种情况仍然会触发静态断言失败,但情况 1 和情况 3 都可以正常工作:
{
std::unique_ptr<float> test = nullptr;
std::unique_ptr<float> test2 = std::move(test); // Works fine
}
{
//std::unique_ptr<float,void(*)(float*)> test = nullptr; // triggers a static assert failure
//std::unique_ptr<float,void(*)(float*)> test2 = std::move(test);
}
{
std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::unique_ptr<float,std::function<void(float*)>> test2 = std::move(test); // Works fine
}
您尝试使用的 unique_ptr
构造函数,default-constructs 删除器,是 ill-formed(在 C++17 之前)或被 SFINAE 禁用(从 C++ 开始) 17) 如果删除器类型是指针,为了防止你不小心创建了一个unique_ptr
,它的删除器本身就是一个空指针。如果你真的想创建这样一个unique_ptr
,你可以通过显式传递一个空删除器来实现:
std::unique_ptr<float,void(*)(float*)> test(nullptr, nullptr);
这个unique_ptr
对象不是很有用,因为它不能删除任何东西。
通过使用空 std::function
删除器,您已经告诉编译器 "yes, I really want to shoot myself in the foot"。当然,当最后一个 std::shared_ptr
被销毁时,空 std::function
被调用,并且发生未定义的行为。您还期待什么?
我会重复 Brian 的回答并补充说,在这种情况下,函数不应该为空,您可以使用函数 reference,,它像所有 C++ 一样不可为空引用,而不是函数指针。
void delete_float(float *f) {delete f;}
using Deleter = void(float*);
// Function pointers
constexpr void(*fptr)(float*) = delete_float;
constexpr Deleter *fptr_typedef = delete_float;
constexpr auto fptr_auto = delete_float;
constexpr auto *fptr_auto2 = delete_float;
// Function references
constexpr void(&fref)(float*) = delete_float;
constexpr Deleter &fref_typedef = delete_float;
constexpr auto &fref_auto = delete_float;
关于函数引用,您需要牢记的一个问题是 lambda 隐式转换为函数指针,而不是函数引用,因此您需要使用取消引用运算符 *
将 lambda 转换为功能参考。
const Deleter *fptr_lambda = [](float *f) {delete f;};
// const Deleter &fref_lambda = [](float *f) {delete f;}; // error
const Deleter &fref_lambda_fixed = *[](float *f) {delete f;};
我想将 NULL std::unique_ptr 移动到 std::shared_ptr,像这样:
std::unique_ptr<float> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
据我所知,这样做应该是合法的,并且在 Visual Studio 2015 和 GCC 中 运行 没问题。
但是,我不能对具有删除器声明的 std::unique_ptr 执行相同的操作,如下所示:
std::unique_ptr<float,void(*)(float*)> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
上面的代码不会在 visual studio 中编译,并且会触发静态断言失败 "error C2338: unique_ptr constructed with null deleter pointer"。
我可以改用 std::function 删除器,在这种情况下可以避免静态断言失败:
std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
在这种情况下,代码可以正常编译,但是一旦 std::shared_ptr 副本 test2被摧毁。
为什么后两种情况这么有问题?
奇怪的是,如果我将 test2 的类型从 std::shared_ptr 更改为 std::unique_ptr,第二种情况仍然会触发静态断言失败,但情况 1 和情况 3 都可以正常工作:
{
std::unique_ptr<float> test = nullptr;
std::unique_ptr<float> test2 = std::move(test); // Works fine
}
{
//std::unique_ptr<float,void(*)(float*)> test = nullptr; // triggers a static assert failure
//std::unique_ptr<float,void(*)(float*)> test2 = std::move(test);
}
{
std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::unique_ptr<float,std::function<void(float*)>> test2 = std::move(test); // Works fine
}
您尝试使用的 unique_ptr
构造函数,default-constructs 删除器,是 ill-formed(在 C++17 之前)或被 SFINAE 禁用(从 C++ 开始) 17) 如果删除器类型是指针,为了防止你不小心创建了一个unique_ptr
,它的删除器本身就是一个空指针。如果你真的想创建这样一个unique_ptr
,你可以通过显式传递一个空删除器来实现:
std::unique_ptr<float,void(*)(float*)> test(nullptr, nullptr);
这个unique_ptr
对象不是很有用,因为它不能删除任何东西。
通过使用空 std::function
删除器,您已经告诉编译器 "yes, I really want to shoot myself in the foot"。当然,当最后一个 std::shared_ptr
被销毁时,空 std::function
被调用,并且发生未定义的行为。您还期待什么?
我会重复 Brian 的回答并补充说,在这种情况下,函数不应该为空,您可以使用函数 reference,,它像所有 C++ 一样不可为空引用,而不是函数指针。
void delete_float(float *f) {delete f;}
using Deleter = void(float*);
// Function pointers
constexpr void(*fptr)(float*) = delete_float;
constexpr Deleter *fptr_typedef = delete_float;
constexpr auto fptr_auto = delete_float;
constexpr auto *fptr_auto2 = delete_float;
// Function references
constexpr void(&fref)(float*) = delete_float;
constexpr Deleter &fref_typedef = delete_float;
constexpr auto &fref_auto = delete_float;
关于函数引用,您需要牢记的一个问题是 lambda 隐式转换为函数指针,而不是函数引用,因此您需要使用取消引用运算符 *
将 lambda 转换为功能参考。
const Deleter *fptr_lambda = [](float *f) {delete f;};
// const Deleter &fref_lambda = [](float *f) {delete f;}; // error
const Deleter &fref_lambda_fixed = *[](float *f) {delete f;};