实现任意容器

Implementing an any container

本文关键字:任意容 实现      更新时间:2023-10-16

我很清楚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兼容,而VS2010具有来自C++11的有限功能。

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

container c;
if (thingKnownAtRunTime()) {
  c = vector<int>;
} else {
  c = vector<string>;
}
c.push_back("hello world");

不过,对于filebuf的情况,您可能会使用类似(警告:未经测试)的东西

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,这可以做得更干净一些,尤其是在不需要转换任何内容的情况下。