2016-03-03 57 views
1

考虑与不能直接存储的成员,例如一类,因为它不会有一个默认的构造函数,封装类的构造函数没有足够的信息来创建它:间接成员RAII:unique_ptr还是可选的?

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.建立一个自动化他们之间选择的类是否有意义?

具体来说,看着exception guarantees for the ctor of std::unique_ptrexception guarantees for the ctor of std::experimental::optional,它似乎很清楚,前一定要进行动态分配和释放 - 运行速度不足,在某些(对齐的)内存缓冲,后者店的东西 - 尺寸方面的缺点。这些是唯一的折衷?

如果这确实是权衡,并鉴于这两种类型的共享足够的接口(构造函数,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为返回类型,但问题和答案专注于每一个传递的功能,这是不相关的,这些私有成员的用户)

回答

1

IMO还有其他的权衡,在这里打球。

  • unique_ptr不可复制或可复制分配,而optional是。
    我想你可以做的一件事就是让indirect_RAII成为一个类型并有条件地添加定义,以便通过调用Bar的拷贝来拷贝定义,即使当选择unique_ptr时。 (或者相反,当它是一个可选项时禁用复制。)
  • optional类型可以有一个constexpr构造函数 - 在编译时你无法真正做到与unique_ptr等价的东西。
  • Bar在构建unique_ptr<Bar>时可能不完整。当optional<Bar>已知时,它不可能不完整。在你的例子中,我猜你认为Bar是完整的,因为你采取了它的大小,但可能你可能想要实现一个类,使用indirect_RAII,如果情况并非如此。
  • 即使在Bar很大的情况下,您仍然可能会发现当选择optional时,std::vector<Foo>将比unique_ptr的效果更好。我希望这发生在vector被填充一次的情况下,然后迭代多次。

也许,作为一般的经验法则,您的规则规则适用于您的程序常用,但我想对于“常用”,您选择哪一个并不重要。使用indirect_RAII类型的另一种方法是,在每种情况下选择一个或另一个,并在您将利用“通用接口”的地方,在需要时将该类型作为模板参数传递。在性能关键领域,请手动进行适当的选择。

+0

谢谢。对于你的第一点,我猜'shared_ptr'也是一个竞争者呢? –

+0

是的,我一开始也这么认为,但是语义从根本上发生了变化,对吧?由于它不复制底层数据。如果你只是用'const'类型来使用它,那么我想它是可以的。但你不想如将另一个数据成员添加到您的类中,然后程序的某个其他部分突然从'optional'切换到'shared_ptr'并打破一些代码。 –

相关问题