临时绑定到 r 值引用会产生错误
Binding temporary to r-value reference produces error
我试图在不使用 unique_ptr 的情况下编写 pImpl。我在写这样的东西时不明白:
class PublicClass
{
public:
// Some stuff
PublicClass();
private:
class ImplClass;
ImplClass&& mImpl;
};
class PublicClass::ImplClass
{
public:
ImplClass() {}
};
PublicClass::PublicClass() : mImpl(ImplClass()){}
产生以下编译错误
Reference member 'mImpl' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object
一边写一边
PublicClass::PublicClass() : mImpl(std::move(ImplClass())){}
没问题。 R 值引用不应延长临时对象的生命周期,如第一个片段?
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
- A temporary bound to a reference member in a constructor's ctor-initializer ([class.base.init]) persists until the constructor exits.
这适用于您的两个示例。也就是说,在您给定的两种情况下,您都有一个 悬空引用。 只是在您示例的情况 2 中,编译器无法为我们提供适当的 error/warning。
首先你必须明白每个对象都需要存储。您有 3 个存储空间:
- 堆栈
- 堆
- 静态存储(定义全局变量的地方)
PublicClass
和 PublicClass::ImplClass
都是 class 并且要创建此 class 的实例,您需要存储空间。
所以你首先决定你想把ImplClass
分配到哪里。
如果您希望能够在堆栈上分配 PublicClass
和 PublicClass::ImplClass
,编译器必须在 编译时知道 ImplClass
的大小。我的意思是,如果在创建对象的编译时不知道对象的大小,则不能在堆栈上分配对象。你可以做的是 pre-allocate 使用 char[N]
变量
的内存
class PublicClass
{
// must be large enough to fit the ImplClass
static constexpr auto PublicClassImplSize = 128;
// The storage for ClassImpl
char alignas(void*) impl_[PublicClassImplSize];
class ImplClass;
public:
PublicClass();
~PublicClass();
};
// cpp
#include <new>
class PublicClass::ImplClass
{
char buf1[10];
// char buf2[10000];
};
PublicClass::PublicClass()
{
static_assert(sizeof(ImplClass) <= PublicClassImplSize);
new (impl_) ImplClass();
}
PublicClass::~PublicClass()
{
reinterpret_cast<ImplClass*>(impl_)->~ImplClass();
}
int main()
{
PublicClass o;
}
如果你不关心ImplClass
分配在哪里,你可以在堆上分配它。在这种情况下,您使用 new/delete
运算符来 allocate/release 内存并在 PublicClass
中实现 RAII 来管理资源。
unique_ptr
是RAII-class的例子。如果出于任何原因您不想使用它,则必须在 PublicClass
内实现 RAII。 IE。您实现了在堆上分配 ClassImpl
的构造函数,并实现了释放资源的析构函数。您还必须关心 move/copy 构造函数和 move/assignment 运算符,因为 C++ 语言提供的默认行为在这里不起作用。
class PublicClass
{
class ImplClass;
ImplClass* impl_{nullptr};
public:
PublicClass();
~PublicClass();
PublicClass(PublicClass&&) noexcept;
PublicClass& operator=(PublicClass&&) noexcept;
};
// cpp
#include <new>
#include <memory>
class PublicClass::ImplClass
{
char buf1[10];
// char buf2[10000];
};
PublicClass::PublicClass()
{
auto impl = std::make_unique<ImplClass>();
// ... more initialization
// Initialization is completed
impl_ = impl.release();
}
PublicClass::PublicClass(PublicClass&& obj) noexcept
: impl_(std::exchange(obj.impl_, nullptr))
{
}
PublicClass& PublicClass::operator=(PublicClass&& obj) noexcept
{
delete impl_;
impl_ = std::exchange(obj.impl_, nullptr);
return *this;
}
PublicClass::~PublicClass()
{
delete impl_;
}
int main()
{
PublicClass o;
}
如果根据设计您只有一个对象实例,则可以在全局命名空间中分配 ClassImpl
。就个人而言,我不喜欢这个解决方案。
更新
I mean you cannot allocate the object on the stack if the size of the object is not known at compile time at the point where object is created
alloca
函数的使用超出范围:)
我试图在不使用 unique_ptr 的情况下编写 pImpl。我在写这样的东西时不明白:
class PublicClass
{
public:
// Some stuff
PublicClass();
private:
class ImplClass;
ImplClass&& mImpl;
};
class PublicClass::ImplClass
{
public:
ImplClass() {}
};
PublicClass::PublicClass() : mImpl(ImplClass()){}
产生以下编译错误
Reference member 'mImpl' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object
一边写一边
PublicClass::PublicClass() : mImpl(std::move(ImplClass())){}
没问题。 R 值引用不应延长临时对象的生命周期,如第一个片段?
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
- A temporary bound to a reference member in a constructor's ctor-initializer ([class.base.init]) persists until the constructor exits.
这适用于您的两个示例。也就是说,在您给定的两种情况下,您都有一个 悬空引用。 只是在您示例的情况 2 中,编译器无法为我们提供适当的 error/warning。
首先你必须明白每个对象都需要存储。您有 3 个存储空间:
- 堆栈
- 堆
- 静态存储(定义全局变量的地方)
PublicClass
和 PublicClass::ImplClass
都是 class 并且要创建此 class 的实例,您需要存储空间。
所以你首先决定你想把ImplClass
分配到哪里。
如果您希望能够在堆栈上分配 PublicClass
和 PublicClass::ImplClass
,编译器必须在 编译时知道 ImplClass
的大小。我的意思是,如果在创建对象的编译时不知道对象的大小,则不能在堆栈上分配对象。你可以做的是 pre-allocate 使用 char[N]
变量
class PublicClass
{
// must be large enough to fit the ImplClass
static constexpr auto PublicClassImplSize = 128;
// The storage for ClassImpl
char alignas(void*) impl_[PublicClassImplSize];
class ImplClass;
public:
PublicClass();
~PublicClass();
};
// cpp
#include <new>
class PublicClass::ImplClass
{
char buf1[10];
// char buf2[10000];
};
PublicClass::PublicClass()
{
static_assert(sizeof(ImplClass) <= PublicClassImplSize);
new (impl_) ImplClass();
}
PublicClass::~PublicClass()
{
reinterpret_cast<ImplClass*>(impl_)->~ImplClass();
}
int main()
{
PublicClass o;
}
如果你不关心ImplClass
分配在哪里,你可以在堆上分配它。在这种情况下,您使用 new/delete
运算符来 allocate/release 内存并在 PublicClass
中实现 RAII 来管理资源。
unique_ptr
是RAII-class的例子。如果出于任何原因您不想使用它,则必须在 PublicClass
内实现 RAII。 IE。您实现了在堆上分配 ClassImpl
的构造函数,并实现了释放资源的析构函数。您还必须关心 move/copy 构造函数和 move/assignment 运算符,因为 C++ 语言提供的默认行为在这里不起作用。
class PublicClass
{
class ImplClass;
ImplClass* impl_{nullptr};
public:
PublicClass();
~PublicClass();
PublicClass(PublicClass&&) noexcept;
PublicClass& operator=(PublicClass&&) noexcept;
};
// cpp
#include <new>
#include <memory>
class PublicClass::ImplClass
{
char buf1[10];
// char buf2[10000];
};
PublicClass::PublicClass()
{
auto impl = std::make_unique<ImplClass>();
// ... more initialization
// Initialization is completed
impl_ = impl.release();
}
PublicClass::PublicClass(PublicClass&& obj) noexcept
: impl_(std::exchange(obj.impl_, nullptr))
{
}
PublicClass& PublicClass::operator=(PublicClass&& obj) noexcept
{
delete impl_;
impl_ = std::exchange(obj.impl_, nullptr);
return *this;
}
PublicClass::~PublicClass()
{
delete impl_;
}
int main()
{
PublicClass o;
}
如果根据设计您只有一个对象实例,则可以在全局命名空间中分配 ClassImpl
。就个人而言,我不喜欢这个解决方案。
更新
I mean you cannot allocate the object on the stack if the size of the object is not known at compile time at the point where object is created
alloca
函数的使用超出范围:)