C++ 无法将删除为NULL的std::unique\u ptr移动到std::shared\u ptr?

C++ 无法将删除为NULL的std::unique\u ptr移动到std::shared\u ptr?,c++,c++14,move,unique-ptr,C++,C++14,Move,Unique Ptr,我想将空std::unique\u ptr移动到std::shared\u ptr,如下所示: std::unique_ptr<float> test = nullptr; std::shared_ptr<float> test2 = std::move(test); std::unique_ptr<float,void(*)(float*)> test = nullptr; std::shared_ptr<float> test2 = std:

我想将空std::unique\u ptr移动到std::shared\u ptr,如下所示:

std::unique_ptr<float> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
std::unique_ptr<float,void(*)(float*)> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
std::unique_ptr test=nullptr;
std::shared_ptr test2=std::move(测试);
据我所知,这样做应该是合法的,而且在VisualStudio2015和GCC中运行良好

但是,我不能对具有deleter声明的std::unique_ptr执行相同的操作,如下所示:

std::unique_ptr<float> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
std::unique_ptr<float,void(*)(float*)> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
std::unique_ptr test=nullptr;
std::shared_ptr test2=std::move(测试);
上面的代码将不会在visual studio中编译,并将触发静态断言失败“错误C2338:使用null deleter指针构造的唯一\u ptr”

我可以使用std::function deleter,在这种情况下,可以避免静态断言失败:

std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);
std::unique_ptr test=nullptr;
std::shared_ptr test2=std::move(测试);
在本例中,代码可以很好地编译,但一旦销毁test2的最后一个std::shared_ptr副本,我就会得到一个中止

为什么后两种情况如此成问题

奇怪的是,如果我将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
}
{
std::unique_ptr test=nullptr;
std::unique_ptr test2=std::move(test);//工作正常
}
{
//std::unique_ptr test=nullptr;//触发静态断言失败
//std::unique_ptr test2=std::move(测试);
}
{
std::unique_ptr test=nullptr;
std::unique_ptr test2=std::move(test);//工作正常
}

您尝试使用的
唯一\u ptr
构造函数默认构造删除器,如果删除器类型是指针,则该构造函数的格式错误(在C++17之前)或被SFINAE禁用(从C++17开始),以防止您意外创建删除器本身为空指针的
唯一\u ptr
。如果确实要创建这样一个
唯一的\u ptr
,可以通过显式传递空的删除器来实现:

std::unique_ptr<float,void(*)(float*)> test(nullptr, nullptr);
std::unique_ptr测试(nullptr,nullptr);
这个
unique_ptr
对象不是很有用,因为它不能删除任何内容


通过使用null
std::function
deleter,您已经告诉编译器“是的,我真的想自杀”。当然,当最后一个
std::shared_ptr
被销毁时,会调用null
std::function
,并发生未定义的行为。你还期待什么?

< p>我会回应布瑞恩的回答,并补充说,在这种情况下,函数不应该是空的,你可以使用一个函数引用,它是不可忽略的,就像所有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;};

所有这些对我来说都没有意义,因为使用具有空对象指针的移动的
唯一的
构造
共享的
与默认构造
共享的
相同,即它仍然保持空对象指针。那么,如果删除器也是空的呢?通过从一个空的
唯一的\u ptr
构造一个空的
共享的\u ptr
,您真正想要实现什么?