如何在未来的施工中留下未构造的类成员,并将其置于新的位置

如何在未来的施工中留下未构造的类成员,并将其置于新的位置

问题描述:

我想创建一个包含应该保持未构造的私有成员的模板,直到使用新的位置明确构建它为止。如何在未来的施工中留下未构造的类成员,并将其置于新的位置

如何用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(); 
     } 
}; 
+2

班级建设是一个全或无的命题。要么整个​​类被构造,要么没有构造。没有回旋的余地。你试图解决什么是真正的问题。不,不是关于不构建集体成员的问题,而是你认为解决方案涉及不构建单个集体成员的问题。 –

+0

@SamVarshavchik实现std ::可选 – FSMaxB

+1

我讨厌这样说,但是......'malloc'。请参阅:https://stackoverflow.com/questions/8959635/malloc-placement-new-vs-new – NathanOliver

没有比尼尔更清洁的方式,利用工会般类:

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可以在其他状态下编码,或者如果有多个成员由同一个标志管理,这仍然有用。

+0

哇,我从来没有想过使用一个工会,但现在我看到它非常简单直接。 – FSMaxB

+0

@FSMaxB其实不是。你只需要替代必须知道所有涉及非平凡类型的工会的规则。由于这基本上从来没有出现,我宁愿坚持放置新的和对齐的存储。另外,关于避免'bool'开销的评论是相当基础的,不管你是否100%正交于union vs aligned_storage。 –

+0

@NirFriedman确实,我并不真正理解关于避开布尔开销的评论。这没有什么意义。使用联合来自C背景确实对我有意义。但是现在我想到了,构建联合成员甚至可能是未定义的行为。 – FSMaxB

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。然而,实现这个需要更多的复杂性,例如像条件继承这样的技巧(只是一种可能的方法)。

+0

Downvoter,解释一下? –

+0

我不知道是谁低估了这一点,但我猜是因为如果'construct'被称为两次,那将是UB。如果它已被构建,我需要一个布尔标志。 – FSMaxB

+0

是的......我以为你会研究可选实现的其他部分,只需要这一点帮助。 downvoter期望我在我的答案中实现所有可选项吗? –