重置方法中指向不完整的共享指针需要删除器

Shared pointer to incomplete needs deleter in reset method

我正在使用 shared_ptr 存储 C 库的指针。 这是一个包含 header bar.h:

的 C 库示例
#pragma once

typedef struct Flupp MyFlupp;

MyFlupp *
create_flupp();

void 
del_flupp(MyFlupp * fp);

void
print_flupp(MyFlupp * f);

这里的结构有一个前向声明并在 bar.so 中定义。 我在我的 C++ 代码中使用 bar.so:

#include <memory>

extern "C"{
#include "bar.h"
}

int main()
{
    std::shared_ptr<MyFlupp> flupp_ptr(nullptr, del_flupp);
    flupp_ptr.reset(create_flupp());

    print_flupp(flupp_ptr.get());
    return 0;
}

这里我将 MyFlupp* 存储在 shared_ptr 中。在声明中,MyFlupp* 是未知的并设置为 nullptr。稍后我调用重置操作来设置有效指针。但是当我编译代码时,出现以下错误:

In file included from /usr/include/c++/8/bits/shared_ptr.h:52,
                 from /usr/include/c++/8/memory:81,
                 from test_foo.cpp:1:
/usr/include/c++/8/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*) [with _Yp = Flupp; <template-parameter-2-2> = void; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’:
/usr/include/c++/8/bits/shared_ptr_base.h:1293:4:   required from ‘std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> std::__shared_ptr<_Tp, _Lp>::reset(_Yp*) [with _Yp = Flupp; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2; std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> = void]’
test_foo.cpp:10:35:   required from here
/usr/include/c++/8/bits/shared_ptr_base.h:1126:19: error: invalid application of ‘sizeof’ to incomplete type ‘Flupp’
    static_assert( sizeof(_Yp) > 0, "incomplete type" );

当我为重置操作提供删除器时,它正在工作。

flupp_ptr.reset(create_flupp(), del_flupp);

谁能给我解释一下这是怎么回事?我已经看过@cppreference,但没有找到答案。

问题是 Flupp 类型只被转发声明,但没有定义。在此处使用的上下文中,它被认为是不完整类型

这对 std::shared_ptr:

的使用有一定的影响

std::shared_ptr may be used with an incomplete type T. However, the constructor from a raw pointer (template<class Y> shared_ptr(Y*)) and the template<class Y> void reset(Y*) member function may only be called with a pointer to a complete type (note that std::unique_ptr may be constructed from a raw pointer to an incomplete type).

Source: cppreference.com

相反,您需要使用接受指针 删除器作为参数的相应重载。

对于 unique_ptr,这不是必需的,因为它将自定义删除器存储为类型的一部分。但是使用 shared_ptr 删除器是类型擦除的,只能在运行时恢复。这允许您在调用 reset 时更改现有 shared_ptr 的删除器。出于这个原因,您总是需要在调用 reset 时重新声明要使用的删除器。如果没有给出删除器,每次调用 reset 也会 implicitly reset the deleter to just calling delete on the managed pointer.

因此,要使其正常工作,只需将您的 reset 调用更改为

flupp_ptr.reset(create_flupp(), del_flupp);