根据我的理解,使用typedef是为STL容器命名的首选方法.
但是,typedef本身并非没有问题.首先,它们不能轻易地向前声明,并且两个typedef可能意外地是相同的类型.
请考虑以下事项:
typedef std::vector<int> vec_a_t; typedef std::vector<float> vec_b_t; void func(const vec_a_t& v); void func(const vec_b_t& v);
根据逻辑类型vec_a_t或vec_b_t,这两个函数的行为应该不同
这种情况将正常工作,直到有人将vec_a_t更改为
typedef std::vector<float> vec_a_t;
现在调用func()突然变得模棱两可. func()的一个现实例子是
std::ostream& operator<<(std::ostream& ost, const vec_a_t& v);
现在,如果我们改为继承
class Vector : public std::vector<int> {}; std::ostream& operator<<(std::ostream& ost, const Vector& v);
也可以宣布
class Vector2 : public std::vector<int> {}; std::ostream& operator<<(std::ostream& ost, const Vector2& v);
这显然是消除歧视.
但是因为std :: vector没有从它们派生的虚拟析构函数,所以这是错误的并且可能导致问题.
相反,我们尝试
class Vector : private std::vector<int> { public: using::size; //Add more using declarations as needed. };
在C 11之前,也存在问题,因为我们必须重新声明构造函数,并且可以继承我们的Vector类.
但是在C 11中,可以执行以下操作
class Vector final : private std::vector<int> { public: using std::vector<value_type>::vector; using std::vector<value_type>::size; //More using directives as needed. };
从我所看到的,这解决了很多问题,为什么不应该从STL容器派生出来.它还具有以下好处:
>它是一种独特的类型,如果其基础类型发生更改,则不会导致模糊调用.
>它可以向前宣布.
>无需为内部成员类型重新实现转发方法.
>它将表现为真正的STL容器(如果使用相关/所有方法公开).
>它的方法可以被覆盖,例如,跟踪对push_back的调用
基于前面的讨论,我的问题是:
>你有没有看到像这样导出STL容器有什么问题
C 11?
>我错过了什么或者这种编码风格会导致任何问题
下线?
>如果这种新类型具有自己的状态,是否会导致任何问题
(例如跟踪对push_back的调用次数)?
编辑:
我知道标准答案是“使用私人领域”.我想知道C 11中提出的解决方案的实际缺点是什么?私有领域的缺点是必须重新实现一系列方法,这些方法只是转发到底层类型.
这种方法也不是一种选择.
class Vector { private: std::vector<int> m_type public: std::vector<int>& get_type(){return m_type;} };
编辑:
不要在最终解决方案中使用typedef coll_t来避免我的新typedef导致问题的答案,只是为了简化输入.
struct BobsContainer { typedef std::vector<int> data_type; data_type data; };
我们现在有一个类型化的std :: vector< int>.是的,访问它需要输入.data.,但作为交换,我们摆脱了很多样板和恶劣的行为.
如果我们想构造底层的std :: vector,对于隐式构造函数我们只需:
{ {blah, blah, blah} }
这确实更喜欢通过标准构造函数调用列表初始化,因此:
{ std::vector<int>( 3 ) }
如果我们想要避免它们,可以使用它们.
隐藏你是一个std :: vector是相对没有意义的.如果您的实现是“我是一个秘密的std :: vector并且我将所有方法重定向到它”,请跳过这个秘密?
确实,您可以通过隐藏一些std :: vector< int>来强制执行一些不变量.但不是其他人:但如果你走得那么远,那就去私人解决方案吧.编写转发方法,特别是在C 1y中,变得非常容易:
template<typename... Args> decltype(auto) insert( Args&&... args ) { return data.insert( std::forward<Args>(args)... ); }
这比使用std :: vector< int> :: insert;更多样板,但只有一点.作为交换,你不再使用’is-a’和继承做奇怪的事情.
对于具有const和非const重载的方法:
template<typename... Args> decltype(auto) insert( Args&&... args ) const { return data.insert( std::forward<Args>(args)... ); }
如果你想变得非常愚蠢,请加入&&和&重载(标准容器尚未使用它们).
因此,如果您要转发几乎所有内容,只需包含一个向量.如果您隐藏几乎所有内容,只需包含一个私有向量并转发.只有在一个奇怪的不稳定的情况下,你隐藏了大约一半的类,并暴露另一半使用容器;和私有继承合理.
当您想要在通用代码中利用空基类优化时,通过继承进行组合非常重要.否则通常不是一个好主意.没有设计为继承的标准容器.