间接成员 RAII:unique_ptr 还是可选的?

Indirect Member RAII: unique_ptr or optional?

考虑具有无法直接存储的成员的 class,例如,因为它没有默认构造函数,并且封闭的 class 的构造函数没有足够的创建它的信息:

class Foo
{
public:
    Foo(){} // Default ctor
private:
    /* Won't build: no default ctor or way to call it's 
        non-default ctor at Foo's ctor. */
    Bar m_bar; 
};

显然,m_bar 需要以不同方式存储,例如,通过指针。不过,std::unique_ptr 似乎更好,因为它会自动销毁它:

    std::unique_ptr<Bar> m_bar;

也可以使用 std::experimental::optional,不过:

    std::experimenatl::optional<Bar> m_bar;

我的问题是:1. 权衡是什么?和 2. 构建一个 class 自动在它们之间进行选择是否有意义?

具体来说,查看 exception guarantees for the ctor of std::unique_ptr and the exception guarantees for the ctor of std::experimental::optional,似乎很清楚前者必须执行动态分配和释放 - 运行时速度劣势,而后者将内容存储在一些(对齐的)内存缓冲区中 - 大小劣势。这些是唯一的权衡吗?

如果这些确实是权衡,并且考虑到两种类型共享足够多的接口(ctor,operator*),使用类似

的东西在它们之间自动进行选择是否有意义
 template<typename T>
 using indirect_raii = typename std::conditional<
     // 20 - arbitrary constant
     sizeof(std::experimental::optional<T>) > 
         20 + sizeof(std::exerimental::optional<T>)sizeof(std::unique_ptr<T>),
     std::unique_ptr<T>,
     std::experimental::optional<T>>::type;

(注:有一个question discussing the tradeoffs between these two as return类型,但是问答的重点是各自向函数的调用者传达了什么,与这些私有成员无关。)

IMO 这里还有其他权衡:

  • unique_ptr 不可复制或不可复制分配,而 optional 是。
    我想你可以做的一件事是使 indirect_RAII 成为 class 类型并有条件地添加定义以通过调用 Bar 的复制构造函数使其可复制,即使 unique_ptr 是选择。 (或者相反,当它是可选的时禁用复制。)
  • optional 类型可以有一个 constexpr 构造函数——你不能在编译时用 unique_ptr 做同样的事情。
  • Bar 在构造 unique_ptr<Bar> 时可能不完整。在知道optional<Bar>的时候不能不完整。在您的示例中,我猜您假设 Bar 是完整的,因为您采用了它的大小,但您可能希望在实际情况并非如此的情况下使用 indirect_RAII 实现 class。
  • 即使在 Bar 很大的情况下,您仍然可能会发现,例如选择 optionalstd::vector<Foo> 的性能会比选择 unique_ptr 时更好。我希望在 vector 被填充一次,然后迭代多次的情况下发生这种情况。

作为一般经验法则,您的大小规则可能适合在您的程序中普遍使用,但我想对于 "common use",您选择哪一个并不重要。使用 indirect_RAII 类型的另一种方法是,在每种情况下选择一个或另一个,并且在您可以利用 "generic interface" 的地方,在必要时将类型作为模板参数传递。在性能关键区域,手动做出适当的选择。