简体   繁体   English

具有非成员函数的std :: vector中的多态

[英]Polymorphism in std::vector with non-member functions

I've already tried to post this problem, but everyone complained, how my problem is hard to understand and asked me to provide an MCVE, so I've decided to ask this question again with an example provided: 我已经尝试过发布此问题,但是每个人都抱怨我的问题很难理解,并要求我提供MCVE,因此,我决定再次提出一个示例,并提供以下示例:

http://pastebin.com/SvidcrUi http://pastebin.com/SvidcrUi

I have a problem, that I came across. 我遇到了一个问题。 I am working in C++11 with Root, and I have a std::vector which contains variables (histograms) of type TH1D* and TH2D*. 我正在C ++ 11中使用Root,并且我有一个std :: vector,其中包含TH1D *和TH2D *类型的变量(直方图)。 I am not allowed to touch the TH1D or TH2D definitions, but I am doing different things based on the type of the variable. 我不允许接触TH1D或TH2D定义,但是根据变量的类型,我正在做不同的事情。

Whenever I want to call an overloaded function, that would handle both cases, my compiler cries out saying "call of overloaded function is ambiguous"... I know it is, but I need help figuring out a better design... How should I do it? 每当我想调用重载函数(能处理这两种情况)时,我的编译器都会大声说“重载函数的调用是模棱两可的” ...我知道,但是我需要帮助找出更好的设计...应该如何可以吗

My problem in the code: 我在代码中的问题:

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 )
}

So, as for the example, my goal is to write functions that can achieve what the message_of_instances() function should do. 因此,作为示例,我的目标是编写可以实现message_of_instances()函数应做的功能的函数。 The example now does not compile, but the only thing it has a problem with, is that it can not deduce the type of the elements in the std :: vector. 该示例现在无法编译,但是唯一有问题的地方是,它无法推断std :: vector中元素的类型。 If I were to call a member function of the elements, like simply write() works. 如果我要调用元素的成员函数,就像简单的write()一样。

My question is: Is there a workaround for these kinds of problems? 我的问题是:这些问题是否有解决方法?

Thank you for all the constructive comments!! 谢谢您提出的所有建设性意见!!

You can use boost::variant to wrap the particular types (the boost variant then keeps track, which type it has been created from). 您可以使用boost :: variant来包装特定类型(然后,boost变体会跟踪创建它的类型)。 Then either check for each value which type it actually is (boost::variant::which) or better yet a variant visitor to apply operations on the particular types. 然后要么检查每个值的实际类型(boost :: variant ::哪个),要么更好地是一个变体访问者,以对特定类型应用操作。

Or you can indeed roll something simpler similar by yourself (basically a wrapper providing constructor for each possible type and keeping track which type it has been constructed from, that is what boost::variant does in the principle). 或者,您确实可以自己滚动一些更简单的类似的东西(基本上是一个包装器,为每种可能的类型提供构造函数,并跟踪构造该类型的类型,这就是boost :: variant的原理)。 Or use union (boost::variant is C++ replacement of unions). 或使用联合(boost :: variant是联合的C ++替换)。

EDIT: This is an example how it can be done without altering the classes. 编辑:这是一个无需更改类即可如何完成的示例。 Basically introducing a wrapper which will store the type-erased implementation keeping track of the actual type (just wrote it quickly, might need some polishing): 基本上,引入一个包装器,该包装器将存储对类型进行擦除的实现,以跟踪实际的类型(只是将其写得很快,可能需要一些改进):

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;
};

Then, use std::vector<BaseWrapper> instead of std::vector<Base *> and call doStuff which will forward the calls up to the BaseWrapper::Impl providing the real call with the correct type. 然后,使用std::vector<BaseWrapper>代替std::vector<Base *>并调用doStuff,它将把调用转发到BaseWrapper :: Impl,以提供具有正确类型的实际调用。 Could be extended to provide RAII for the Base * as well, call the different methods through functors etc. 可以扩展为Base *提供RAII,通过函子等调用不同的方法。

EDIT #2: With the boost::variant it would look like this: 编辑#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);
    }
}

As you can see, far more elegant, however it has the limitation that you need to know all the possible types upfront (the boost::variant needs to know about all of them and they must be supplied as the template arguments). 如您所见,它优雅得多,但是它的局限性在于您需要预先知道所有可能的类型(boost :: variant需要了解所有这些类型,并且必须将它们作为模板参数提供)。 The above wrapper solution does not have that limitation, but that comes with a price - there are extra memory allocations (boost::variant does not need any extra memory allocations) and virtual method calls (boost::variant static_visitor uses the template machinery so the calls are direct). 上面的包装器解决方案没有此限制,但是要付出一定的代价-存在额外的内存分配(boost :: variant不需要任何额外的内存分配)和虚拟方法调用(boost :: variant static_visitor使用模板机制,因此电话是直接的)。

Note that the visitor can either provide a global templated visit method (as it is in the example), or provide separate operator () for each type. 请注意,访问者可以提供全局模板化的访问方法(如示例中所示),也可以为每种类型提供单独的运算符()。 Or even combine both (have separate operator for some types and templated solution for the rest). 甚至可以将两者结合使用(某些类型需要单独的运算符,其余类型则需要模板化解决方案)。

In the fact, the wrapper solution can be extended as well to use a visitor (instead of having to provide extra methods to call each different method). 实际上,包装解决方案也可以扩展为使用访问者(而不是必须提供额外的方法来调用每个不同的方法)。

In order to properly utilize polymorphism, you need to have all of your functions declared in your base class which can then be overridden in the derived classes. 为了正确利用多态性,您需要在基类中声明所有函数,然后可以在派生类中重写这些函数。

If that doesn't work, you can do one of the following: 如果这不起作用,则可以执行以下操作之一:

  • Downcast your base class to derived classes using dynamic_cast . 使用dynamic_cast将基类转换为派生类。

  • Utilize the Visitor pattern. 利用Visitor模式。 You can look up it's definition and use on this site and others. 您可以在此站点和其他站点上查找其定义并使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM