【问题标题】:boost::variant and polymorphism in c++11c++11 中的 boost::variant 和多态性
【发布时间】:2017-11-02 20:42:18
【问题描述】:

我正在 c++11 中尝试多态性和 boost::variant

这里是代码

#include <iostream>
#include <boost/variant.hpp>
using namespace std;

class Polygon {
protected:
        int width, height;
public:
        void set_values (int a, int b)
        {
                width=a;
                height=b;
        }
};

class Rectangle: public Polygon {
public:
        Rectangle() {
                std::cout << "ctor rectangle" << std::endl;
        }

        int area()
        {
                return width*height;
        }
};

class Triangle: public Polygon {
public:
        Triangle() {
                std::cout << "ctor triangle" << std::endl;
        }
        int area()
        {
                return width*height/2;
        }
};


int main () {

        Triangle r;
        boost::variant<Rectangle, Triangle> container = r;
        int x = 4;
        int y = 5;
        if (container.type() == typeid(Rectangle)) {
                r.set_values(x,y);
                std::cout << r.area() << std::endl;
        } else if ( container.type() == typeid(Triangle)){
                r.set_values(x,y);
                std::cout << r.area() << std::endl;
        }

        return 0;


}

我想知道这是否是最好的方法。代码中有重复(在 main() 函数中),对于每种类型(我们在运行时获取类型)我们执行相同的操作,即设置值并打印区域。

有没有更好的方法来做到这一点?

【问题讨论】:

  • 为什么不在Polygon 中添加virtual int area()?我认为这里不需要variant;在这个例子中,正则多态可以正常工作。
  • 另外,RTTI 通常是一个坏主意,在使用variant 时应该是不必要的。您想改用访问者。
  • 如果问题是“如何消除代码中的重复”,这个问题似乎很好。但是您的最后陈述说“有没有更好的方法来做到这一点?”,这太宽泛/不清楚了。如果您的意思是“我如何调用boost::variant 的方法”,那也很好(不包括骗子)。明确你的问题
  • C++14 让这变得更好。你被 C++11 困住了吗?

标签: c++ c++11 boost boost-variant


【解决方案1】:

不要使用 if-else 结构。

看看boost。我在下面输入了一个未经测试的小示例。

#include "boost/variant.hpp"
#include <iostream>

class my_visitor : public boost::static_visitor<void>
{
public:
    void operator()(Rectangle const & i) const
    {
        // do something here
    }

    void operator()(Triangle const & i) const
    {
        // do something here
    }
};

int main()
{
    boost::variant< Triangle, Rectangle > u(Triangle());
    boost::apply_visitor( my_visitor(), u );
}

【讨论】:

  • 嗯,这不会建立,或者至少它不应该建立。您的访问者返回一个int,不应将其绑定到auto&amp;
  • 这是未经测试的快速代码,并不完美,但足以理解。
  • 看起来存在根本缺陷?您不能从变体访问者返回两种不同的类型。并且两者都不会转换为 int。它违反了const。我不确定您要做什么,我可以写一个变体访问者。实际代码有 9 行,其中大约一半包含或导致编译时错误。
  • 好的,好的,我已经改变了一点,所以也许它是可编译的。至少它更正确。如你所愿
【解决方案2】:

当您需要基于值类型变体的多态性时,这是一个辅助类。

template<class Base>
struct poly_ptr_t : boost::static_visitor<Base*> {
  template<class T>
  Base* operator()(T& t)const { return std::addressof(t); }

  template<class...Ts>
  Base* operator[](boost::variant<Ts...>& v) const {
    return boost::apply_visitor( *this, v );
  }
  template<class...Ts>
  Base const* operator[](boost::variant<Ts...> const& v) const {
    return boost::apply_visitor( *this, v );
  }
};

用途:

poly_ptr_t<Polygon> as_polygon;
int main() {
  boost::variant<Triangle, Rectangle> u(Triangle{});
  as_polygon[u]->set_values(x,y);
}

现在,area 有点痛苦。获取父级 Polygon 将无济于事,因为它没有区域。

如果我们添加了

virtual int area() = 0;

然后到Polygon

std::cout << as_polygon[v]->area();

突然起作用了。

替代方案在 C++11 中有点混乱。在具有适当 boost 支持的 C++14 中,我们得到:

std::cout << boost::apply_visitor( [](auto& e){return e.area();}, v );

boost::apply_visitor( [](auto& e){std::cout << e.area();}, v );

我们使用通用 lambda 调用 area

或者我们可以写一个区域访问者:

struct get_area : boost::static_visitor<int> {
  template<class T>
  int operator()(T& t)const{ return t.area(); }
};

现在我们可以这样做了:

std::cout << boost::apply_visitor( get_area, v );

在这些情况下,我们在 main 中都没有代码重复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    • 1970-01-01
    • 1970-01-01
    • 2014-05-04
    相关资源
    最近更新 更多