C++ C++;std::unique#ptr:为什么不';lambdas没有尺码费吗?
我正在读“有效的现代C++”。在与C++ C++;std::unique#ptr:为什么不';lambdas没有尺码费吗?,c++,c++11,c++14,unique-ptr,C++,C++11,C++14,Unique Ptr,我正在读“有效的现代C++”。在与std::unique_ptr相关的项目中,说明如果自定义删除器是无状态对象,则不会发生大小费用,但如果是函数指针或std::function大小费用,则会发生大小费用。你能解释一下原因吗 假设我们有以下代码: auto deleter_ = [](int *p) { doSth(p); delete p; }; std::unique_ptr<int, decltype(deleter_)> up(new int, deleter_); auto
std::unique_ptr
相关的项目中,说明如果自定义删除器是无状态对象,则不会发生大小费用,但如果是函数指针或std::function
大小费用,则会发生大小费用。你能解释一下原因吗
假设我们有以下代码:
auto deleter_ = [](int *p) { doSth(p); delete p; };
std::unique_ptr<int, decltype(deleter_)> up(new int, deleter_);
autodeleter][](int*p){doSth(p);delete p;};
std::unique_ptr up(新int,deleter_u);
据我所知,
unique\u ptr
应该有一个类型为decltype(deleter\u)
的对象,并将deleter\u
分配给该内部对象。但很明显,事实并非如此。您能用尽可能小的代码示例解释一下这背后的机制吗?如果删除程序是无状态的,则不需要存储空间。如果删除程序不是无状态的,则状态需要存储在unique\u ptr
本身中。std::function
和函数指针具有仅在运行时可用的信息,因此这些信息必须存储在对象中,与对象本身的指针一起。这反过来需要分配(在unique_ptr
本身中)空间来存储该额外状态
也许了解这些将有助于您了解如何在实践中实现这一点。类型特征是如何实现这一点的另一种可能性
显然,库编写者如何实现这一点取决于他们以及标准允许的内容。来自
独特的\u ptr
实现:
template<class _ElementT, class _DeleterT = std::default_delete<_ElementT>>
class unique_ptr
{
public:
// public interface...
private:
// using empty base class optimization to save space
// making unique_ptr with default_delete the same size as pointer
class _UniquePtrImpl : private deleter_type
{
public:
constexpr _UniquePtrImpl() noexcept = default;
// some other constructors...
deleter_type& _Deleter() noexcept
{ return *this; }
const deleter_type& _Deleter() const noexcept
{ return *this; }
pointer& _Ptr() noexcept
{ return _MyPtr; }
const pointer _Ptr() const noexcept
{ return _MyPtr; }
private:
pointer _MyPtr;
};
_UniquePtrImpl _MyImpl;
};
模板
类唯一\u ptr
{
公众:
//公共接口。。。
私人:
//使用空基类优化以节省空间
//使用默认值创建唯一的\u ptr\u删除与指针相同的大小
类_UniquePtrImpl:private deleter_类型
{
公众:
constexpr_UniquePtrImpl()noexcept=默认值;
//其他一些构造器。。。
deleter_type&_deleter()无例外
{return*this;}
常量deleter\u type&\u deleter()常量noexcept
{return*this;}
指针&\u Ptr()无异常
{return_MyPtr;}
常量指针\u Ptr()常量noexcept
{return_MyPtr;}
私人:
指针_MyPtr;
};
_Uniqueptrimplu MyImpl;
};
\u UniquePtrImpl
类包含指针并从deleter\u类型派生而来
如果deleter恰好是无状态的,则可以优化基类,使其本身不占用字节。然后整个unique\u ptr
可以与包含的指针大小相同,即:与普通指针大小相同。Aunique\u ptr
必须始终存储其删除器。现在,如果删除器是一个没有状态的类类型,那么unique\u ptr
可以使用,这样删除器就不会使用任何额外的空间
具体的实现方式各不相同。例如,和MSVC都将托管指针和删除器存储在中,如果所涉及的类型之一是空类,则会自动获得空基优化
从上面的libc++链接
template <class _Tp, class _Dp = default_delete<_Tp> >
class _LIBCPP_TYPE_VIS_ONLY unique_ptr
{
public:
typedef _Tp element_type;
typedef _Dp deleter_type;
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
private:
__compressed_pair<pointer, deleter_type> __ptr_;
模板
类_LIBCPP_TYPE_VIS_ONLY unique_ptr
{
公众:
类型定义_Tp元素_类型;
typedef_Dp deleter_type;
typedef typename uu指针u type::type指针;
私人:
__压缩对(ptr);
libstdc++在std::tuple
中,一些谷歌搜索建议他们的tuple
实现采用空基优化,但我找不到任何文档如此明确地说明
在任何情况下,演示了libc++和libstdc++都使用EBO来减小带有空删除器的唯一\u ptr
的大小。事实上,对于非无状态的lambda,即捕获一个或多个值的lambda,将有一个大小惩罚
但对于非捕获lambda,有两个关键事实需要注意:
- lambda的类型是唯一的,只有编译器知道
- 非捕获lambda是无状态的
因此,编译器能够完全根据lambda的类型调用lambda,该类型被记录为unique_ptr
类型的一部分;不需要额外的运行时信息
这就是为什么不捕获lambda是无状态的。就大小惩罚问题而言,与任何其他无状态删除函子类型相比,不捕获lambda当然没有什么特别之处
请注意,std::function
不是无状态的,这就是为什么相同的推理不适用于它
最后,请注意,虽然无状态对象通常需要具有非零大小以确保它们具有唯一的地址,但无状态基类不需要添加到派生类型的总大小中;这称为空基优化。因此可以实现unique\u ptr
(如Bo Perrson的回答)作为从deleter类型派生的类型,如果它是无状态的,则不会造成大小惩罚。(实际上,这可能是正确实现unique\u ptr
而不对无状态deleter造成大小惩罚的唯一方法,但我不确定。)实现可以对其内部数据使用空基类优化,从而使无状态删除对象“消失”通过从中派生一些其他结构。好的,我已经添加了一个示例。据我所知,lambda不是无状态的。但是还要注意,lambda类型是唯一的,因此只要编译器知道用作删除器的lambda的确切类型,如果lambda实际上没有捕获任何内容(这一个没有),从理论上讲,它应该拥有编译时编写删除代码所需的所有信息,而不需要占用运行时空间。不过,我不知道这是否可以保证。@KyleStrand嗯,通过我的谷歌搜索,我发现lambdas不捕获任何内容是无状态的,而其他则是有状态的;您的inf