C++ 共享\u ptr析构函数,复制和不完整类型

C++ 共享\u ptr析构函数,复制和不完整类型,c++,c++11,shared-ptr,incomplete-type,C++,C++11,Shared Ptr,Incomplete Type,我有这样一个头文件foo.h(不相关的内容省略): 在getBar()的const重载上(或者实际上是在从该重载实例化的标准库的深处) 我的问题是,这一警告是否可以被安全地忽略 在我看来,在getBar()const中调用了std::shared_ptr的两个成员函数:转换构造函数和析构函数 // converting constructor template <class Y> std::shared_ptr<const Bar>::shared_ptr(std::sh

我有这样一个头文件
foo.h
(不相关的内容省略):

getBar()
的const重载上(或者实际上是在从该重载实例化的标准库的深处)

我的问题是,这一警告是否可以被安全地忽略

在我看来,在
getBar()const
中调用了
std::shared_ptr
的两个成员函数:转换构造函数和析构函数

// converting constructor
template <class Y>
std::shared_ptr<const Bar>::shared_ptr(std::shared_ptr<Y> &&r)
27.2.2.2§1规定,当销毁的共享指针为空时,没有副作用

我理解为什么我会收到警告-析构函数代码还必须注意在存储指针上调用
delete
时的情况,并且该代码确实会删除不完整的类型。但在我看来,在我的情况下永远无法达到它,因此
getBar()const
是安全的


我是对的,还是我忽略了一个调用或其他可能导致
getBar()const
实际删除不完整类型的东西?

否;这一警告不能被完全忽略。您的代码创建了一个共享的\u ptr对象。shared_ptr构造函数是创建和存储删除器的模板。通过添加代码在头中创建共享的_ptr,您过早地实例化了模板构造函数

shared_ptr使用的deleter技巧允许您在定义类之前声明它们,但是在您首次使用它们之前,它们仍然需要查看完整的类型。你的代码不能保证调用Bar的析构函数。更糟糕的是,今天,它甚至可以工作,但可能是一个定时炸弹,使一个非常难以调试的错误出现在您的代码中

问题与代码中指针的内容无关。只要您有创建共享指针的代码,而该指针看不到Bar的完整定义就足够了

修复很容易。在完成Bar声明之后,只需将该代码放入您的实现文件中


编辑:g++给出的警告适合这种情况,但代码不适合。现在,我将把这个答案留到我有时间调查之前(或者其他人找出g++为什么给出这个警告)。

我找不到这个警告的理由。我也不能用clang/libc++复制警告

一般来说,给定一个
shared\u ptr
,如果看不到使用
Bar*
shared\u ptr
的构造,以及可选的删除器,就无法确定
~Bar()
是否调用过。没有办法知道
shared\u ptr
中存储了什么删除程序,并且给定了
shared\u ptr
中存储的一些未知删除程序
d
,沿着
Bar*
(比如
p
)的一侧,
d(p)
调用
~Bar()

例如,您的
条可能没有可访问的析构函数:

class Bar
{
    ~Bar();
};
您的
Foo::getBar()
可以这样实现:

warning C4150: deletion of pointer to incomplete type 'Bar'; no destructor called
std::shared_ptr<Bar>
Foo::getBar()
{
    // purposefully leak the Bar because you can't call ~Bar()
    return std::shared_ptr<Bar>(new Bar, [](Bar*){});
}
std::共享
Foo::getBar()
{
//由于无法调用~Bar(),所以故意泄漏该条
返回std::shared_ptr(新条,[](条*){});
}
编译器无法在没有看到foo.cpp的情况下知道

在我看来,此警告类似于编译器错误,或者可能是
std::shared\u ptr
实现中的错误


你能无视这个警告吗?我不知道。在我看来,您正在处理实现中的一个bug,因此该bug很可能意味着警告是真实的。但是假设是完全一致的实现,我认为在您所展示的代码中不需要将
Bar
作为完整类型。

const_cast(this)->getBar()是一个坏把戏-你最好从非常量版本调用常量版本。@ikh我永远无法理解这个参数。在我的例子中,如果非常量版本修改了
*此
,你就完蛋了。在你的例子中,如果const版本返回一个指向实际声明的
const
的指针,你就完蛋了。两者都有一个失败点,您只需要进行一次额外的强制转换。在const调用non-const时,由类的用户确保不在const对象上调用const版本。在非const调用const时,由类的创建者来确保逻辑有效。不要让你的类成为没有检查你的逻辑的用户的炸弹。@NeilKirk我认为它不会给用户带来负担,它只是要求类的实现者不要在非常量重载中修改
*这个
。如果它没有修改
*这个
,那么函数应该是常量。哎呀,现在你有两个常量函数了!我不认为采用
shared\u ptr
的构造函数实际上创建了一个deleter——它与参数共享所有权,因此它也必须使用deleter(或缺少deleter)。请注意,使用原始指针的ctor要求指向的类型是完整的,但使用
shared\u ptr
的ctor则不是。我说的不是“使用shared\u ptr的构造函数”——而是shared\u ptr的构造函数本身。这是一个模板构造函数——它的工作原理与模板函数类似,因为它将调用参数与模板参数匹配,并基于此派生要使用的类型。一旦模板匹配,它将实例化构造函数。该构造函数创建了删除器,正是该删除器看到了不完整的类型。这正是g++发出此警告的原因。但我只是从问题代码中的另一个
共享ptr
构造
共享ptr
。链接中的重载(10)是否会创建一个删除器?不要这样认为。。。至少在标准上。是的,我的手机界面很薄,看不清你在做什么。但这是g++针对这种情况给出的警告,因此实现可能仍有待尝试
class Bar
{
    ~Bar();
};
std::shared_ptr<Bar>
Foo::getBar()
{
    // purposefully leak the Bar because you can't call ~Bar()
    return std::shared_ptr<Bar>(new Bar, [](Bar*){});
}