Axe*_*ega 6 c++ inheritance stl vector c++11
我一直在考虑从C++ 11中继承STL容器.我知道如果没有一些考虑因素就不应该这样做,因为没有虚拟析构函数.
根据我的理解,使用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);
Run Code Online (Sandbox Code Playgroud)
这两个函数的行为应根据逻辑类型vec_a_t
或不同而有所不同vec_b_t
这种情况将正常工作,直到有人改变vec_a_t
到
typedef std::vector<float> vec_a_t;
Run Code Online (Sandbox Code Playgroud)
现在打电话func()
突然变得含糊不清.一个现实的例子func()
是
std::ostream& operator<<(std::ostream& ost, const vec_a_t& v);
Run Code Online (Sandbox Code Playgroud)
现在,如果我们改为继承
class Vector : public std::vector<int>
{};
std::ostream& operator<<(std::ostream& ost, const Vector& v);
Run Code Online (Sandbox Code Playgroud)
也可以宣布
class Vector2 : public std::vector<int> {};
std::ostream& operator<<(std::ostream& ost, const Vector2& v);
Run Code Online (Sandbox Code Playgroud)
这显然是消除歧视.
但是,因为std::vector
没有从它们派生的虚拟析构函数,所以这是错误的并且可能导致问题.
相反,我们尝试
class Vector : private std::vector<int>
{
public:
using::size;
//Add more using declarations as needed.
};
Run Code Online (Sandbox Code Playgroud)
在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.
};
Run Code Online (Sandbox Code Playgroud)
从我所看到的,这解决了很多问题,为什么不应该从STL容器派生出来.它还具有以下好处:
using
).push_back
基于前面的讨论,我的问题是:
你是否认为在C++ 11中导出这样的STL容器有什么问题?
我错过了什么或者这种编码风格会导致任何问题吗?
如果这种新类型具有自己的状态(例如跟踪调用次数),是否会导致任何问题push_back
?
编辑:
我知道标准答案是"使用私人领域".我想知道C++ 11中提出的解决方案的实际缺点是什么?私有领域的缺点是必须重新实现一系列方法,这些方法只是转发到底层类型.
这种方法也不是一种选择.
class Vector
{
private:
std::vector<int> m_type
public:
std::vector<int>& get_type(){return m_type;}
};
Run Code Online (Sandbox Code Playgroud)
编辑:
不要typedef coll_t
在最终解决方案中使用以避免我的新typedef导致问题的答案,它只是为了简化键入.
struct BobsContainer {
typedef std::vector<int> data_type;
data_type data;
};
Run Code Online (Sandbox Code Playgroud)
我们现在有一个打字std::vector<int>
.是的,访问它需要打字.data.
,但作为交换,我们摆脱了很多样板和讨厌的行为.
如果我们想构建底层的std::vector
,对于隐式构造函数,我们只需:
{ {blah, blah, blah} }
Run Code Online (Sandbox Code Playgroud)
这确实更喜欢通过标准构造函数调用列表初始化,因此:
{ std::vector<int>( 3 ) }
Run Code Online (Sandbox Code Playgroud)
如果我们想要避免它们,可以使用它们.
隐藏自己是一个std::vector
相对毫无意义的人.如果您的实现是"我是一个秘密std::vector
,我将所有方法重定向到它",请跳过这个秘密?
确实,你可以通过隐藏一些std::vector<int>
而不是其他的来强制执行一些不变量:但如果你要走得那么远,那就去private
解决问题吧.编写转发方法,特别是在C++ 1y中,变得非常容易:
template<typename... Args> decltype(auto) insert( Args&&... args ) { return data.insert( std::forward<Args>(args)... ); }
Run Code Online (Sandbox Code Playgroud)
这是一个比标准更多的样板using std::vector<int>::insert;
,但只是一点点.作为交换,你不再使用'is-a'和继承做奇怪的事情.
对于具有两个const
和非const
重载的方法:
template<typename... Args> decltype(auto) insert( Args&&... args ) const { return data.insert( std::forward<Args>(args)... ); }
Run Code Online (Sandbox Code Playgroud)
如果你想变得非常愚蠢,包含&&
和&
重载(标准容器还没有使用它们).
因此,如果您要转发几乎所有内容,只需包含一个vector
.如果您隐藏几乎所有内容,只需包含私有vector
和转发.只有在奇怪的不稳定的情况下,你隐藏了大约一半的类并暴露另一半,using container;
并且private
继承是合理的.
当您想要在通用代码中利用空基类优化时,通过继承进行组合非常重要.否则通常不是一个好主意.没有设计为继承的标准容器.
有多种方法可以解决 的重载问题func
。如果您甚至同意这是一个问题;我不这样做:“如果有人更改typedef foo A;
我typedef bar A;
的程序可能会停止编译”是无法避免的事情。更改 typedef 的人有责任检查他们在做什么。
无论如何,解决方案是使用:
template<typename T> void func(const std::vector<T> &v)
Run Code Online (Sandbox Code Playgroud)
int
然后,当该函数需要与 for不同的功能时,它可以在内部调用重载函数float
。
typedef std::vector<int> coll_t;
您提出的解决方案与您试图避免的问题完全相同!想必您还需要版本coll_a_t
和coll_b_t
,导致VectorA
和 ,VectorB
以便您可以利用重载func
。
但是如果有人将此 typedef 更改为std::vector<float>
?