如何在未来的施工中留下未构造的类成员,并将其置于新的位置
我想创建一个包含应该保持未构造的私有成员的模板,直到使用新的位置明确构建它为止。如何在未来的施工中留下未构造的类成员,并将其置于新的位置
如何用C++ 14实现这一点?
有点像这样:
template <typename T>
class Container {
private:
T member; //should be left unconstructed until construct() is called
public:
Container() = default;
void construct() {
new (&this->member) T();
}
};
没有比尼尔更清洁的方式,利用工会般类:
template <typename T>
class Container {
private:
bool is_constructed = false;
union { T member; };
public:
Container() {}
~Container() {
if (is_constructed) {
member.~T();
}
}
void construct() {
assert(!is_constructed);
new (&this->member) T();
is_constructed = true;
}
};
您可能还需要增加其他的构造函数/赋值运算符。当然,在这种简单的情况下,std::optional
完全一样,但更干净。如果你想躲开bool
的开销,如果is_constructed
可以在其他状态下编码,或者如果有多个成员由同一个标志管理,这仍然有用。
template <typename T>
class Container {
private:
std::aligned_storage_t<sizeof(T), alignof(T)> data;
bool is_active = false;
T& as_type() { return reinterpret_cast<T&>(data); }
public:
Container() = default;
void construct() {
if (is_active) throw ...;
new (&data) T();
is_active = true;
}
};
使用as_type
构造之前的类型(或它被破坏后)将是UB。虽然我可能实际上不会有会员construct
。我只想着重写下你需要的不同成员。在这种情况下,你应该只是有一个构造函数一个T
:
Container::Container(const T& t)
: is_active(true)
{
new (&datamember) T(t);
}
有一点要注意的是,你不停地实现,你会开始看到一些这种方法的缺点的(在其最简单形成)。当然,你必须有一个布尔值来检查事物是否已经构造,等等。所以你会有析构函数,复制,移动运算符,检查一个布尔值,然后做一些事情。
但是,如果包含的类型本身是微不足道的,就像整数或其他东西一样,这些都不需要。在某些情况下,这可能会造成非常大的性能下降。所以标准要求这些特征从包含的类型传递到可选的类型。也就是说,如果包含的类型是可以破坏的,那么这个类型也是可选的。因此:
std::cerr << std::is_trivially_destructible_v<std::optional<int>>;
将打印出1
。然而,实现这个需要更多的复杂性,例如像条件继承这样的技巧(只是一种可能的方法)。
Downvoter,解释一下? –
我不知道是谁低估了这一点,但我猜是因为如果'construct'被称为两次,那将是UB。如果它已被构建,我需要一个布尔标志。 – FSMaxB
是的......我以为你会研究可选实现的其他部分,只需要这一点帮助。 downvoter期望我在我的答案中实现所有可选项吗? –
班级建设是一个全或无的命题。要么整个类被构造,要么没有构造。没有回旋的余地。你试图解决什么是真正的问题。不,不是关于不构建集体成员的问题,而是你认为解决方案涉及不构建单个集体成员的问题。 –
@SamVarshavchik实现std ::可选 – FSMaxB
我讨厌这样说,但是......'malloc'。请参阅:https://stackoverflow.com/questions/8959635/malloc-placement-new-vs-new – NathanOliver