实现Stream <T>,可以将其转换为Stream <U>,其中U是T的基础

Zel*_*lta 5 c++ pointers casting

我正在尝试实现对象的通用输入流.也就是说,实现的接口或轻量级代理.实现的细节是未知的,即我的库的用户可以编写自己的例如protobuf消息流,将其传递给我的库并返回,例如,字符串流或任何其他流.我想保持流通用的接口,以便用户可以编写自己的转换并构建转换管道.

流的接口应如下所示:

template <typename T>
class Stream {
public:
    T* input();
}
Run Code Online (Sandbox Code Playgroud)

在每次调用时,input()如果流为空,则应返回流中的下一个对象或空指针.

问题是,我想Stream<T>是转换为Stream<U>,如果T*是转换为U*.

我不成功的尝试是使用指针实现如下:

class StreamImplBase {
public:
    virtual void* input_raw() = 0;
}

template <typename T>
class StreamImpl: public StreamImplBase {
public:
    void* input_raw() final { return input(); }
    virtual T* input() = 0;
}

template <typename T>
class Stream {
    StreamImplBase* impl;
public:
    Stream(StreamImpl<T>* impl): impl(impl) {}
    T* input() { return static_cast<T*>(impl->input_raw()); }
}
Run Code Online (Sandbox Code Playgroud)

从构造StreamImpl<T>保证了void*从返回input_raw()被铸造收购T到void*因此static_cast<T*>是安全的.

但是,如果我执行任何转换,则此声明不会成立.也就是说,建设Stream<T>从StreamImpl<U>是不安全的,即使U*是转换为T*.

所以我的问题是,我该如何处理这个问题?

我看到了下一个可能性:

  • std::function<T*(void*)>在流中存储转换器(例如)并在每次转换时更新它.这似乎不必要地昂贵;

  • 存储结果static_cast<U*>((T*)0)并将此结果添加到从中获取的指针input_raw().这似乎是不必要的危险;

  • 添加第二个模板参数OrigT并存储StreamImpl<OrigT>*而不是存储StreamImplBase*.这将限制课程的可能应用,我想避免;

  • 使用dynamic_cast是不是一种选择,因为一个不能dynamic_cast从void*.

还有其他可能性吗?其他人如何实现这样的事情?


这是一个用例.假设我们有一个protobuf消息X.我想要这个工作:

Stream<X> stream = ...;
Stream<google::protobuf::Message> raw_stream = stream;
Run Code Online (Sandbox Code Playgroud)

再说一遍,我不知道如何Stream<X>实施.我所知道的是它包含一个指向生成消息的实现的共享指针.

Yak*_*ont 2

这:

template <typename T>
class Stream {
public:
  T* input();
};
Run Code Online (Sandbox Code Playgroud)

是一个具有一个操作的对象,它接受 0 个参数并返回一个T*。

也是这样的:

std::function<T*()>
Run Code Online (Sandbox Code Playgroud)

诚然,您调用它就像stream()而不是stream.input().

使用第二个解决方案,如果U是 的基数T,那么您可以将上面的转换为std::function<U*()>。这解决了你的问题。

就我个人而言,我认为.input在流名称之间输入()不值得做很多工作。

其他人已经完成的类型擦除是最好的类型擦除。