c++ - 具有非成员函数的 std::vector 中的多态性

标签 c++ c++11 vector polymorphism

我已经尝试发布这个问题,但是每个人都提示我的问题很难理解,并要求我提供一个 MCVE,所以我决定再次提出这个问题并提供一个例子:

http://pastebin.com/SvidcrUi

我遇到了一个问题。我在 C++11 中与 Root 一起工作,我有一个 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() 一样有效。

我的问题是:是否有解决此类问题的方法?

谢谢大家的建设性意见!!

最佳答案

您可以使用 boost::variant 来包装特定类型(然后 boost 变体会跟踪它是从哪种类型创建的)。然后检查每个值的实际类型 (boost::variant::which) 或者更好的是变体访问者对特定类型应用操作。

或者你确实可以自己推出一些更简单的类似东西(基本上是一个包装器,为每种可能的类型提供构造函数并跟踪它是从哪个类型构造的,这就是 boost::variant 在原理中所做的)。或者使用 union(boost::variant 是 union 的 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()。或者甚至将两者结合起来(对某些类型使用单独的运算符,对其余类型使用模板化解决方案)。

事实上,包装器解决方案也可以扩展为使用访问者(而不是必须提供额外的方法来调用每个不同的方法)。

关于c++ - 具有非成员函数的 std::vector 中的多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34109903/

相关文章:

c++ - 如何创建具有不可复制和不可分配对象的 vector ?

c++ - 错误 : expected a type, 得到分配器

c++ - 基类对派生类指针的引用

c++ - rsyslog 写入同步文件不会阻塞

c++ - 位集、 bool vector 或简单大整数的整数 vector

c++ - 为什么 vector<bool> 不是 STL 容器?

c++ - 即使使用 -g 标志进行编译,gdb 中也没有调试符号

c++ - 查找特定年份和闰年的特定日期

c# - C# 的 C++ 代码帮助

c++: 函数不能重载