实现任何容器

Posted

技术标签:

【中文标题】实现任何容器【英文标题】:Implementing an any container 【发布时间】:2013-07-19 03:09:34 【问题描述】:

我很清楚 boost::anyboost::variant,但在这种情况下,它们不符合我的需求。

通常,要包含未知类型的对象,可以从公共基础派生它并通过虚拟方法访问实例。但是,如果不能使用一个共同的基地怎么办?

我知道在这个例子中你必须知道包含的类型,但请耐心等待。 std::vector 是一个模板类,***类也是一个模板。据我所知,如果不修改 STL 标头,就无法为其提供非模板库。现在,假设我想创建一个单一类型的向量,但包含类不关心类型,但它确实需要访问一些“常用”方法,例如size()pop_back()

使用boost::any,类型已被删除,几乎不可能取消引用包含的对象。 boost::varianttuple 需要事先知道可以插入哪些类型,从而导致包含类本身就是一个模板。

到目前为止,我所拥有的是这样的:

struct container

    virtual ~container() = 0;
    virtual void pop_back() = 0;
    virtual size_t size() = 0;
    ...

template < typename T >
struct contained

    std::vector< T > _vec;
    contained ( size_t n, T _what ) : _vec( n, _what ) 
    virtual void pop_back()  _vec.pop_back(); 
    ...

class some_class

    container* _cont;
    template < typename T >
    void create ( T _first )  _cont = new contained< T >(1,_first); 
    ...

这里客户端可以调用create(),模板参数应该是自动确定的。我知道这不是一个很好的例子,但我试图对客户端隐藏模板参数。如果不这样做,some_class 还必须跟踪存储的类型。

我的方法依赖于虚拟调用,这会导致性能损失,尤其是当内部类有自己的虚拟方法时。

还有其他类型的容器更适合我的需要吗?

理想情况下,我想要这样的东西

container = std::vector< T >;
container.pop_back();
container.push_back( T2 ); // compile error if types don't match

它会在内部跟踪类型并进行简单的转换,而不是依赖虚拟方法。几乎就像auto,不同的是一旦声明它的类型可以改变。

编辑:

实际上,我想围绕std::basic_filebuf 创建一个包装器。此包装类根据 BOM 使用 charwchar_tunsigned long 打开文件。包装器也派生自basic_filebuf,模板参数是客户端选择的任何内容。在内部,它将文件中的 unicode 代码点转换为客户端所需的编码。存储内部basic_filebuf 时会出现问题,因为它可以使用任何类型声明为模板参数。我不想使用模板专业化,因为我希望客户端能够传入他们自己的 basic_filebuf 实例。

必须与 VS2010 兼容,C++11 的功能有限。

【问题讨论】:

你想解决什么问题? @GManNickG 一个例子是标准流类,其中元素类型可以是char wchar_t int... 如果不关心内部类型,您将如何存储该模板? std::stringstd::wstring 怎么样?几乎相同,但处理方式略有不同。 对不起,我不关注。我的意思是你的程序最终会做什么 @GManNickG 我的程序试图做什么重要吗?我已经清楚地描述了我想要什么以及我尝试过什么。我正在寻找替代方法来存储任何类型而不会丢失类型信息。 其实我也看不懂你的描述。有这一段“理想情况下,我想要......”这将是一个完美的地方,可以作为您打算如何使用容器的示例。如果我猜对了,那么您想要的是不可能的:考虑第一行(定义基础类型的行)取决于运行时参数。在这种情况下,第三行(可能不匹配的行)不可能导致编译时错误。 【参考方案1】:

这不能与编译时类型检查结合使用。基于您的“理想”示例:

container c;
if (thingKnownAtRunTime()) 
  c = vector<int>;
 else 
  c = vector<string>;

c.push_back("hello world");

不过,对于 filebuf 的情况,你可能会得到足够好的东西,比如 (warning: untested)

template<typename FILEBUF>
void gensputc(FILEBUF* fb, long long c) 
  FILEBUf::char_type c2 = smart_convert<FILEBUf::char_type>(c);
  fb->sputc(c2);


class FileBufWrapper 
public:
  template<typename FILEBUF> FileBufWrapper(FILEBUF* fb) 
    fb_ = fb;
    sputc_ = gensputc<FILEBUF>;
  
  void sputc(long long c) 
    sputc_(fb_,c);
  
private:
  typedef void(*sputc_t)(void*, long long);
  sputc_t sputc_;
  void* fb_;
;

如果值无法转换为 char 类型,smart_convert 函数将引发运行时异常。此外,您需要为您打算调用的每个函数执行此操作。

如果您可以访问 c++11 的 std::function 和 std::bind,这可以变得更简洁一些,尤其是在您不需要转换任何内容的情况下。

【讨论】:

以上是关于实现任何容器的主要内容,如果未能解决你的问题,请参考以下文章

在不使用内置容器的情况下实现视图控制器容器?

Docker 镜像与容器管理

5.1中容器(Container)和门面(Facade)的实现

Docker 添加容器SSH服务

实现一个符合 C++17 的 STL 容器? [关闭]

Docker 数据卷与容器互联