无法将具有NULL删除的std :: unique_ptr移动到std :: shared_ptr?

时间:2018-03-16 21:16:27

标签: c++ c++14 move unique-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中编译,并且会触发静态断言失败“错误C2338:使用空删除指针构造的unique_ptr”。

我可以使用 std :: function 删除器,在这种情况下可以规避静态断言失败:

std::unique_ptr<float,std::function<void(float*)>> test = nullptr;
std::shared_ptr<float> test2 = std::move(test);

在这种情况下,代码编译得很好,但是一旦 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
}

2 个答案:

答案 0 :(得分:4)

你试图使用的unique_ptr构造函数,默认构造删除器,是不正确的(在C ++ 17之前)或由SFINAE(从C ++ 17开始)禁用(如果删除类型)是一个指针,以阻止您意外创建一个unique_ptr,其删除器本身就是一个空指针。如果你真的想创建这样的unique_ptr,可以通过显式传递空删除器来实现:

std::unique_ptr<float,void(*)(float*)> test(nullptr, nullptr);

unique_ptr对象不是很有用,因为它无法删除任何内容。

通过使用null std::function删除器,你已经告诉了编译器&#34;是的,我真的想要自己在脚下拍摄&#34;。当然,当最后一个std::shared_ptr被销毁时,将调用null std::function,并发生未定义的行为。您还期待什么?

答案 1 :(得分:0)

我会回应Brian的回答并在这样的情况下补充一点,在这种情况下函数不应为null,你可以使用一个函数 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;

你需要记住函数引用的一个问题是lambdas隐式转换为函数指针,而不是函数引用,因此你需要使用解引用运算符*将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;};