我一直在考虑从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);
这两个函数的行为应该有所不同,具体取决于逻辑类型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容器派生出来。它还有以下好处:
using
公开相关/所有方法)。push_back
根据之前的讨论,我的问题是:
你认为在这里推导出像这样的STL容器有什么问题吗? C ++ 11
我错过了什么或者这种编码风格会导致任何问题 下线?
如果这种新类型具有自己的状态,是否会导致任何问题
(例如跟踪push_back
的来电次数?
编辑:
我知道标准答案是&#34;使用私人字段&#34;。我想知道C ++ 11中提出的解决方案的实际缺点是什么?私有领域的缺点是必须重新实现一系列直接转化为基础类型的方法。
这种做法也不是一种选择。
class Vector
{
private:
std::vector<int> m_type
public:
std::vector<int>& get_type(){return m_type;}
};
编辑:
不要在最终解决方案中使用typedef coll_t
来避免我的新typedef导致问题的答案,只是为了简化输入。
答案 0 :(得分:5)
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>
而不是其他隐私来强制实施一些不变量:但是如果你走得那么远,那么请使用private
解决方案。编写转发方法,特别是在C ++ 1y中,变得异常简单:
template<typename... Args> decltype(auto) insert( Args&&... args ) { return data.insert( std::forward<Args>(args)... ); }
这是比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)... ); }
如果你想变得非常愚蠢,请加载&&
和&
重载(标准容器尚未使用它们)。
因此,如果您要转发几乎所有内容,只需包含vector
。如果您隐藏几乎所有内容,只需包含私有vector
并转发。只有在你隐藏了大约一半类并暴露另一半的奇怪的不稳定情况下,using container;
和private
继承才会合理。
当您想在通用代码中利用空基类优化时,通过继承进行组合非常重要。否则通常不是一个好主意。没有设计为继承的标准容器。
答案 1 :(得分:3)
有几种方法可以解决func
的重载问题。如果你甚至同意这是一个问题;我不这样做:“如果有人将typedef foo A;
更改为typedef bar A;
,我的程序可能会停止编译”这不是可以避免的事情。更改typedef的人有责任检查他们正在做什么。
无论如何,解决方案是使用:
template<typename T> void func(const std::vector<T> &v)
然后,当函数需要int
的不同功能而不是float
时,函数可以在内部调用重载函数。
typedef std::vector<int> coll_t;
您提出的解决方案与您要避免的问题完全相同!大概你还需要版本coll_a_t
和coll_b_t
,导致VectorA
和VectorB
,这样你就可以利用重载的func
。
但如果有人将此typedef更改为std::vector<float>
?
答案 2 :(得分:-1)
Typedef完全没问题。如果你不小心为同一类型提供了两个不同的typedef,那么你的代码结构是错误的。
但是,如果您有某些原因无法保证typedef的唯一性,可以尝试以下方法: 使用std :: type_traits,您可以检查两个typedef是否完全相同。如果是,您可以使用std :: enable_if来影响第二个声明。
constexpr bool isSame = std::is_same<vec_a_t, vec_b_t>::value;
void func(const vec_a_t& v);
template <typename = std::enable_if<!isSame>::type> func(const vec_b_t& v);