我知道你可以在向量中存储一堆Parent *对象。但是,如果无法在父类中定义需要调用对象的函数(它们依赖于子类的模板参数),那么您应该如何从容器中检索对象?
在这个例子中:
#include <iostream>
class ImageBase{};
template <typename TPixel>
class Image : public ImageBase
{
public:
TPixel GetPixel() const {TPixel a; return a;}
};
template<typename TImage>
void Output(const TImage* image)
{
std::cout << image->GetPixel();
}
int main(int, char *[])
{
// Works correctly
{
Image<float>* image = new Image<float>;
image->GetPixel();
Output(image);
}
{
ImageBase* image = new Image<float>;
Output(image);
}
return 0;
}
输出(图像);其中'image'是ImageBase *失败(当然)因为GetPixel未在ImageBase中定义。我知道你可以dynamic_cast&lt;&gt;到一堆类型来判断子类是否与它们中的任何一个匹配,但是这个列表很快就会变得非常长。如果它可以驻留在一个地方,那么长列表就可以了,但你怎么能做到这个呢?该函数将采用ImageBase *,但它会返回什么?
returnType? GetSubclass(ImageBase* input)
{
if(dynamic_cast<Image<float>*>(input))
{
return Image<float>*;
}
else if(dynamic_cast<Image<int>*>(input))
{
return Image<int>*;
}
}
对我来说,希望能够在子类上调用一些模板函数似乎是合理的,这些子类只能通过模板参数(如本例中的设置)而在签名上有所不同,不是吗?
在我的实际案例中,Image和ImageBase都是库的一部分,因此我无法更改它们。
答案 0 :(得分:3)
访问者模式以恢复类型信息,可能使用实现visit
函数的模板化助手。
首先,让我们将您的算法变成多态仿函数对象:
struct Output
{
std::ostream& dest;
Output(std::ostream& destination) : dest(destination) {}
template<typename PixelType>
void operator()(const Image<PixelType>* image) const
{
dest << image->GetPixel();
}
};
现在,让我们添加一个访问者界面:
struct ImageVisitor /* abstract */
{
virtual void Visit(Image<RGBQUAD>*) const = 0;
virtual void Visit(Image<RGBTRIPLE>*) const = 0;
virtual void Visit(Image<RGBQUAD16>*) const = 0;
virtual void Visit(Image<RGBTRIPLE16>*) const = 0;
virtual void Visit(Image<RGBQUADF>*) const = 0;
virtual void Visit(Image<RGBTRIPLEF>*) const = 0;
virtual void Visit(Image<RGBQUADD>*) const = 0;
virtual void Visit(Image<RGBTRIPLED>*) const = 0;
};
和货运代理人:
template<typename Functor>
struct ImageVisitorShim : ImageVisitor
{
Functor& fn;
ImageVisitorShim(Functor& algorithm) : fn(algorithm) {}
virtual void Visit(Image<RGBQUAD> *im) const { fn(im); }
virtual void Visit(Image<RGBTRIPLE> *im) const { fn(im); }
virtual void Visit(Image<RGBQUAD16> *im) const { fn(im); }
virtual void Visit(Image<RGBTRIPLE16> *im) const { fn(im); }
virtual void Visit(Image<RGBQUADF> *im) const { fn(im); }
virtual void Visit(Image<RGBTRIPLEF> *im) const { fn(im); }
virtual void Visit(Image<RGBQUADD> *im) const { fn(im); }
virtual void Visit(Image<RGBTRIPLED> *im) const { fn(im); }
};
还有一家工厂:
template<typename Functor>
ImageVisitorShim<Functor> MakeImageVisitor(Functor& f) { return f; }
现在是符合访问者标准的图片包装器:
struct VisitableImageBase
{
virtual void VisitWith(const ImageVisitor&) = 0;
};
template<typename PixelType>
struct VisitableImage : VisitableImageBase
{
unique_ptr<Image<PixelType>> content; // or shared or raw pointer, if ownership is elsewhere
VisitableImage(Image<PixelType>* im) : content(im) {}
virtual void VisitWith(const ImageVisitor& v) { v.Visit(content.get()); }
};
最后,您可以使用多态图像矢量!
vector<unique_ptr<VisitableImageBase>> images;
Output outputter(std::cout);
for( auto vim : images ) vim->VisitWith(MakeImageVisitor(outputter));
这是很多代码,但好处是只要使用模板实现仿函数,就可以添加新类型而不会影响现有的仿函数(只需扩展垫片)。并且不需要太多代码来添加更多图像处理函数(只是一个新的模板仿函数类,类似于Output
)。