【问题标题】:Wrapping of C-code with a unique_ptr and custom deleter使用 unique_ptr 和自定义删除器包装 C 代码
【发布时间】:2013-10-03 18:15:11
【问题描述】:

我正在尝试将来自 OpenCV 的 C-API (CvPOSITObject) 的对象包装在智能指针中。据我了解,它应该类似于以下内容:

unique_ptr<CvPOSITObject, decltype(cvReleasePOSITObject)> positObject;
positObject = unique_ptr<CvPOSITObject, decltype(cvReleasePOSITObject)>(cvCreatePOSITObject(param1, param2), cvReleasePOSITObject);

但我得到一个编译器错误,而谷歌并没有真正提供帮助。

这两个函数的声明是:

CVAPI(CvPOSITObject*)  cvCreatePOSITObject( CvPoint3D32f* points, int point_count );
CVAPI(void)  cvReleasePOSITObject( CvPOSITObject**  posit_object );

我得到了类似的东西

1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1227): error C2207: 'std::_Unique_ptr_base<_Ty,_Dx,_Empty_deleter>::_Mydel' : a member of a class template cannot acquire a function type
1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1322): warning C4180: qualifier applied to function type has no meaning; ignored
1>  Myfile.cpp
1>C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1221): warning C4180: qualifier applied to function type has no meaning; ignored
1>          C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\memory(1283) : see reference to class template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,_Empty_deleter>' being compiled
1>          with
1>          [
1>              _Ty=CvPOSITObject,
1>              _Dx=void (CvPOSITObject **),
1>              _Empty_deleter=false
1>          ]
1>          C:\MyDir\Myfile.hpp(71) : see reference to class template instantiation 'std::unique_ptr<_Ty,_Dx>' being compiled
1>          with
1>          [
1>              _Ty=CvPOSITObject,
1>              _Dx=void (CvPOSITObject **)
1>          ]

我该如何正确地做到这一点?

【问题讨论】:

    标签: c++ opencv c++11 smart-pointers unique-ptr


    【解决方案1】:

    您的代码有两个问题。首先,正如 BenVoigt 在他的 answer 中提到的那样,decltype 不会触发从函数类型到函数类型指针的隐式转换,因此您必须显式获取函数的地址。您的代码更改为

    positObject = unique_ptr<CvPOSITObject, 
                             decltype(&cvReleasePOSITObject)>(
                             cvCreatePOSITObject(param1, param2), 
                             cvReleasePOSITObject);
    

    但是,由于不同的原因,这现在将无法编译,这给我们带来了第二个问题。 cvReleasePOSITObject 接受 CvPOSITObject ** 类型的参数,但上面的 unique_ptr 将尝试使用 CvPOSITObject * 调用其删除器。要解决此问题,只需对删除器使用 lambda 表达式即可。

    positObject = unique_ptr<CvPOSITObject, 
                             void(*)(CvPOSITObject *)>(
                             cvCreatePOSITObject(param1, param2), 
                             [](CvPOSITObject *p) { cvReleasePOSITObject(&p); });
    

    如果您想将unique_ptr 的声明和初始化分开,有几个选项。第一个示例演示了如何为此使用 lamda 表达式。

    auto deleter = [](int *p) {
        delete p;
    };
    
    int main()
    {
        std::unique_ptr<int, decltype(deleter)> p(nullptr, deleter);    
        p.reset(new int(42));
    }
    

    您仍然必须将删除器实例传递给unique_ptr,否则它将尝试默认构造删除器,这会失败,因为从 lambda 表达式生成的闭包已删除默认构造器(§5.1.2 /20 来自 N3691)。

    另一种选择是将删除器编写为函子,它允许默认构造。

    struct deleter
    {
        void operator()(int *p) const
        {
            delete p;
        }
    };
    
    int main()
    {
        std::unique_ptr<int, deleter> p;
    
        p.reset(new int(42));
    }
    

    【讨论】:

    • 编译并且看起来就像它应该做的那样。谢谢!尤其是第二部分,非常有帮助!
    • 有什么办法可以拆分变量声明和实际初始化?如果我将所有内容放在一行上,它会起作用,如果我将其拆分,则会收到错误“unique_ptr 构造为空删除器指针”。
    【解决方案2】:

    试试

    decltype(&cvReleasePOSITObject)
    

    存在从函数名到指向该函数的指针的隐式转换,但函数类型和函数指针类型不一样,decltype 不会导致隐式转换。

    【讨论】:

    • 感谢您的解释!
    猜你喜欢
    • 1970-01-01
    • 2016-11-23
    • 2023-03-11
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-09
    • 2015-08-13
    相关资源
    最近更新 更多