从C ++ 11继承STL容器 [英] Inherit from STL container in C++11

查看:279
本文介绍了从C ++ 11继承STL容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在思考从C ++ 11继承STL容器。我知道这不应该没有一些考虑,因为没有虚拟析构函数。



使用typedefs是,从我已经理解的首选方式给名称到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;
//根据需要使用声明添加更多。
};

在C ++ 11之前也有这样的问题,因为我们必须重新声明构造函数



但是在C ++ 11中可以做如下操作:

 类Vector final:private std :: vector< int> 
{
public:
using std :: vector< value_type> :: vector;
using std :: vector< value_type> :: size;
//根据需要使用指令。
};



从我可以看到,这解决了很多的问题,为什么一个不应该从STL容器。它还具有以下优点:


  1. 它是一个不同类型,如果其底层类型更改不会导致模糊调用。 li>
  2. 可以向前声明。

  3. 无需重新实现向内部成员类型转发方法。

  4. 它将作为一个真正的STL容器(如果相关/所有方法都使用使用)。

  5. 可以被覆盖,例如跟踪 push_back

我的问题,基于前面的讨论,




  • 您在
    C ++ 11中看到导出STL容器有什么问题吗? p>



  • 我缺少某些东西, li>

    如果这个新类型有一个自己的状态
    (例如跟踪调用 push_back )会导致任何问题? p>




编辑:



我知道标准答案是Use私有字段。我想知道在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;
};

现在我们有一个类型 std :: vector< int> 。是的,访问它需要键入 .data。,但作为交换,我们摆脱了大量的样板和讨厌的行为。



如果我们要构造底层的 std :: vector ,对于隐式构造函数,我们只需:

  {@ blah,blah,blah}} 

通过标准构造函数调用列表初始化,因此:

  {std :: vector< int>(3)} $ b $ 



可以使用

>隐藏你是一个 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)...) } 

这比使用std :: vector< int> ; :: insert; ,但只有一点。作为交换,你不再用'is-a'和继承来做奇怪的事情。



对于 const 和非 - const 重载:

  template< typename .. 。Args> decltype(auto)insert(Args& ... args)const {return data.insert(std :: forward< Args>(args)...) } 

如果你想真的傻,包括&& & 重载(标准容器尚未使用它们)。



因此,如果你几乎转发了一切,只是包含一个向量。如果你几乎隐藏了一切,只需包含一个私人向量并转发。只有在奇怪的不稳定情况下,你隐藏了大约一半的类,并暴露另一半的使用容器; private 继承获得合理。



当你想在通用代码中利用空基类优化时,通过继承组合是很重要的。它通常不是一个好主意,否则。没有设计为继承的标准容器。


I have been thinking about inheriting from STL container in C++11. I know that this should not be done without some considerations since there is no virtual destructors.

Using typedefs is, from what I have understood the preferred way to give names to STL containers.

However typedefs are not without problems by themselves. First of all they can not easily be forward declared and two typedefs might accidently be the same type.

Ponder the following:

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);

The two functions should behave differently depending on the logical type vec_a_t or vec_b_t

This situation will work fine until someone changes vec_a_t to

typedef std::vector<float> vec_a_t;

Now a call to func() is suddenly ambiguous. A realistic example for func() is

std::ostream& operator<<(std::ostream& ost, const vec_a_t& v);

Now if we instead inherit like

class Vector : public std::vector<int>
{};

std::ostream& operator<<(std::ostream& ost, const Vector& v);

It will be possible do also declare

class Vector2 : public std::vector<int> {};

std::ostream& operator<<(std::ostream& ost, const Vector2& v);

Which will clearly be disambigious.

However since std::vector does not have virtual destructors deriving from them like this is wrong and can cause problems.

Instead we try

class Vector : private std::vector<int>
{
public:
   using::size;
   //Add more using declarations as needed.
};

Before C++11 there was issues with this also, since we would have to redeclare the constructors and it would have been possible to subclass our Vector class.

However in C++11 it is possible to do the following

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.
};

From what I can see this solves a lot of the problems of why one should not derive from STL containers. It also has the following benefits:

  1. It is a distinct type, that can not cause ambiguous calls if its underlying type is changed.
  2. It can be forward declared.
  3. There is no need to re-implement forwarding methods to an internal member type.
  4. It will behave as a true STL container (if the relevant/all methods are exposed with using).
  5. Its methods can be overridden, e.g. to track calls to push_back

My questions, based on the previous discussion, are:

  • Do you see anything wrong with deriving STL containers like this in C++11?

  • Am I missing something or can this style of coding cause any problems down the line?

  • Would it cause any problems if this new type had a state of its own (e.g. track the number of calls to push_back)?

EDIT:

I know the standard answer is "Use a private field". I was wondering what the realistic downsides of the proposed solution in C++11 is? The downside of a private field is to have to re-implement a whole range of methods that just forwards to the underlying type.

This approach would not be an option either.

class Vector
{
private:
   std::vector<int> m_type
public:
   std::vector<int>& get_type(){return m_type;}
};

EDIT:

Do not use the typedef coll_t in the final solution to avoid answers that my new typedef causes problems, it was just there to ease the typing.

解决方案

struct BobsContainer {
  typedef std::vector<int> data_type;
  data_type data;
};

We now have a typed std::vector<int>. Yes, access to it requires typing .data., but in exchange we get rid of a LOT of boilerplate and nasty behavior.

If we want to construct the underlying std::vector, for implicit constructors we simply:

{ {blah, blah, blah} }

this does prefer to invoke the list initialization over standard constructors, so:

{ std::vector<int>( 3 ) }

can be used if we want to avoid them.

Hiding that you are a std::vector is relatively pointless. If your implementation is "I'm a secret std::vector and I redirect all of my methods to it", skip the secret?

It is true you can enforce some invariants by hiding some std::vector<int> but not others: but if you are going that far, go with the private solution. Writing forwarding methods, especially in C++1y, gets ridiculously easy:

template<typename... Args> decltype(auto) insert( Args&&... args ) { return data.insert( std::forward<Args>(args)... ); }

which is a bit more boilerplate than using std::vector<int>::insert;, but only a bit. And in exchange you are no longer doing strange things with 'is-a' and inheritance.

For methods with both const and non-const overloads:

template<typename... Args> decltype(auto) insert( Args&&... args ) const { return data.insert( std::forward<Args>(args)... ); }

and if you want to get really silly, include && and & overloads (standard containers don't use them yet).

So if you are forwarding almost everything, just contain a vector. If you are hiding almost everything, just contain a private vector and forward. Only in the strange unstable case where you are hiding about half of the class and exposing the other half does the using container; and private inheritance get reasonable.

Composition via inheritance is important when you want to exploit the empty base class optimization in generic code. It usually isn't a good idea otherwise. None of the standard containers where designed to be inherited from.

这篇关于从C ++ 11继承STL容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆