我已经尝试发布这个问题,但是每个人都抱怨,我的问题很难理解并要求我提供MCVE,所以我决定再次提出这个问题提供一个例子:
我有一个问题,我遇到过。我在使用Root的C ++ 11中工作,我有一个std :: vector,它包含TH1D *和TH2D *类型的变量(直方图)。我不被允许触及TH1D或TH2D定义,但我根据变量的类型做了不同的事情。
每当我想调用一个可以处理这两种情况的重载函数时,我的编译器大声说“调用重载函数是模糊的”......我知道它是,但我需要帮助找出更好的设计..我应该怎么做?
我在代码中的问题:
void save_histograms_from_vector( const std :: vector<TH1*>& histogram_vector_p )
{
for( auto& histogram: histogram_vector_p )
{
save_histogram_as_canvas( histogram ); //overloaded function
}
}
(...)
template<typename TH_type_1, typename TH_type_2, typename TH_type_3, typename TH_type_4>
void save_as_two_by_two_canvas( TH_type_1 histogram_1_p, TH_type_2 histogram_2_p, TH_type_3 histogram_3_p, TH_type_4 histogram_4_p, TFile* output_file_p, const std :: string& directory_name_p, const std :: string& canvas_name_p, const std :: string& canvas_title_p )
{
(...)
const std :: vector<TH1*> histograms = { histogram_1_p, histogram_2_p, histogram_3_p, histogram_4_p };
save_histograms_from_vector( histograms );
// This would have worked if I called the function Write() for each of the histograms )
}
因此,对于该示例,我的目标是编写可以实现message_of_instances()函数应该执行的操作的函数。 这个例子现在不编译,但它唯一有问题的是,它不能推断出std :: vector中元素的类型。如果我要调用元素的成员函数,就像简单的write()一样。
我的问题是:针对这些问题是否有解决方法?
感谢您提出的所有建设性意见!!
答案 0 :(得分:1)
您可以使用boost :: variant来包装特定类型(boost变量然后跟踪,它是从哪个类型创建的)。然后检查每个值实际上是哪个类型(boost :: variant :: which)或者更好的是变体访问者对特定类型应用操作。
或者你确实可以自己滚动更简单的东西(基本上是一个包装器,为每种可能的类型提供构造函数,并跟踪构造它的类型,这就是boost :: variant在原理中的作用)。或者使用union(boost :: variant是C ++替换联合)。
编辑:这是一个如何在不改变类的情况下完成的示例。基本上引入了一个包装器,它将存储类型擦除的实现,跟踪实际类型(只是快速写入,可能需要一些抛光):
class BaseWrapper
{
public:
template<typename TH_TYPE>
BaseWrapper(TH_TYPE *x)
: impl(createImpl(x))
{}
BaseWrapper()
: impl(nullptr)
{}
BaseWrapper(const BaseWrapper &other)
: impl(cloneImpl(other.impl))
{}
BaseWrapper & operator =(const BaseWrapper &other)
{
if (this != &other)
{
ImplBase *newImpl = cloneImpl(other.impl);
delete impl;
impl = newImpl;
}
return *this;
}
~BaseWrapper()
{
delete impl;
}
void doStuff() const
{
if (impl)
impl->doStuff();
}
private:
class ImplBase {
public:
ImplBase(Base *x)
: ptr(x)
{}
virtual ImplBase *clone() const = 0;
virtual void doStuff() const = 0;
protected:
Base *ptr;
};
template<typename TH_TYPE>
class Impl: public ImplBase {
public:
Impl(Base *x)
: ImplBase(x)
{}
ImplBase *clone() const
{
return new Impl<TH_TYPE>(*this);
}
void doStuff() const
{
if (ptr)
write_and_do_other_stuffs( static_cast<TH_TYPE *>(ptr) );
}
};
template<typename TH_TYPE>
static ImplBase *createImpl(TH_TYPE *x)
{
return new Impl<TH_TYPE>(x);
}
static ImplBase * cloneImpl(ImplBase *impl)
{
return impl ? impl->clone() : impl;
}
ImplBase *impl;
};
然后,使用std::vector<BaseWrapper>
代替std::vector<Base *>
并调用doStuff,它将调用转发到BaseWrapper :: Impl,提供具有正确类型的真实调用。可以扩展为Base *提供RAII,通过仿函数等调用不同的方法。
编辑#2:使用boost :: variant它看起来像这样:
#include <boost/variant.hpp>
typedef boost::variant<Derived_one *, Derived_two *> BaseVariant_t;
struct MyVisitor: boost::static_visitor<>
{
template<typename TH_TYPE>
void operator()(TH_TYPE * ptr) const
{
write_and_do_other_stuffs( ptr );
}
};
void message_of_instances( const std :: vector<BaseVariant_t>& instances_p )
{
for( auto it = instances_p.begin(); it != instances_p.end();++it )
{
boost::apply_visitor(MyVisitor(), *it);
}
}
正如你所看到的,更优雅,但它有一个限制,你需要预先知道所有可能的类型(boost :: variant需要知道所有这些类型,它们必须作为模板参数提供) 。上面的包装器解决方案没有这个限制,但需要付出代价 - 有额外的内存分配(boost :: variant不需要任何额外的内存分配)和虚方法调用(boost :: variant static_visitor使用模板机制,所以电话是直接的。)
请注意,访问者可以提供全局模板化访问方法(如示例中所示),也可以为每种类型提供单独的operator()。或者甚至将两者结合起来(对于某些类型具有单独的运算符,对于其余类型具有模板化解决方案)。
事实上,包装器解决方案也可以扩展为使用访问者(而不是必须提供额外的方法来调用每个不同的方法)。
答案 1 :(得分:1)
为了正确利用多态性,您需要在基类中声明所有函数,然后可以在派生类中重写。
如果这不起作用,您可以执行以下操作之一:
使用dynamic_cast
将基类向下转换为派生类。
使用Visitor
模式。您可以在此网站和其他网站上查找其定义和使用。