如何在基类指针向量的元素上应用重载的多态函数

问题描述 投票:1回答:3

我有一个基类Object

struct Object{
};

n(在本例中为2)从其继承的类

struct Integer : public Object{
  int i_;
  Integer(int i) : i_{i}{}
}

struct Float : public Object{
  float f_;
  Float(float f) : f_{f}{}
}

通过(绝对)使用多态性,我现在可以将这两种类型存储在向量中:

std::vector<Object*> object_list{new Integer(1), new Float(2.1), new Integer(3), new Float(4.2)};

但是现在我想将所有这些值加在一起。

我想起来...

1)...定义功能

Integer* add(Integer* i, Integer* j);
Float*   add(Integer* i, Float* f);
Float*   add(Float* f, Float* g);
Float*   add(Float* f, Integer* i);

但是这需要将Object动态转换为所有可用类型-两次,如果我有足够的子类,这似乎是一场灾难。

2)...模板,但是不起作用,因为类型在编译时未知。

关于以下要求,最有效的方法是什么:

*执行时间比内存使用情况更重要(尽管它应该在8GB系统上运行)

*它应该支持任意数量的子类,但必须至少达到20

*不限于添加,而是应支持任意函数f(Object* a, Object* b)

*类的设计尚未确定。如果某些事情需要更改(或更改自身的总体结构),则可能]

*所有可能的类型都是预先已知的,不需要支持外部DLL

*不需要支持多重继承

* *在错误处理方面不需要很强大。可恢复会很好,但是我可以忍受SEGFAULT。

c++ casting polymorphism overloading
3个回答
3
投票
using Object = std::variant<Float, Integer>;

现在您可以拥有一个std::vector<Object>并在其中存储FloatInteger

struct Integer {
  int val = 0;
  friend std::ostream& operator<<( std::ostream& os, Integer const& obj ) {
    return os << obj.val;
  }        
};
struct Float {
  double val = 0.;
  friend std::ostream& operator<<( std::ostream& os, Float const& obj ) {
    return os << obj.val;
  }        
};

using Object = std::variant<Integer, Float>;
std::ostream& operator<<( std::ostream& os, Object const& obj ) {
  // note: if the type in Object doesn't have a << overload,
  // this will recurse and segfault.
  std::visit( [&]( auto const& e ){ os << e; }, obj );
  return os;
}

Integer add_impl(Integer const& i, Integer const& j) { return {i.val + j.val}; }
Float   add_impl(Integer const& i, Float const& j) { return {i.val + j.val}; }
Float   add_impl(Float const& i, Float const& j) { return {i.val + j.val}; }
Float   add_impl(Float const& i, Integer const& j) { return {i.val + j.val}; }

Object  add( Object const& lhs, Object const& rhs ) {
  return std::visit( []( auto& lhs, auto& rhs )->Object { return {add_impl( lhs, rhs )}; }, lhs, rhs );
}

Test code:

Object a = Integer{7};
Object b = Float{3.14};
Object c = Integer{-100};
Object d = Float{0.0};

std::cout << add( a, b ) << "," << add( b, c ) << "," << add( c, d ) << "," << add( add(a, b), add( c, d ) ) << "\n";

这实现了一个分派表(较新的编译器将生成一个效率更高的表),该表将查找add重载。

返回类型是Object,但在运行时它将包含FloatInteger

您支持的类型列表必须在Object的定义处。这些对象不必是相关类型。

您可以在add_impl中类型的名称空间中而不是在中心位置扩展Object。 ADL将用于查找过载集。

当然是I'd implement operator+ instead of add

有一些技巧可以解决:

operator+

那个问题;基本上像这样:

add

这将阻止// note: if the type in Object doesn't have a << overload, // this will recurse and segfault. 看到namespace ObjectOnly { struct Object; struct Object:std::variant<Integer, Float> { using std::variant<Integer, Float>::variant; std::variant<Integer, Float> const& base() const& { return *this; } std::variant<Integer, Float> & base()& { return *this; } std::variant<Integer, Float> const&& base() const&& { return std::move(*this); } std::variant<Integer, Float> && base()&& { return std::move(*this); } }; Object add_impl( Object const& lhs, Object const& rhs ) { return std::visit( [](auto& lhs, auto& rhs)->Object { return {lhs+rhs}; }, lhs.base(), rhs.base() ); } Object operator+( Object const& lhs, Object const& rhs ) { return add_impl( lhs, rhs ); } std::ostream& stream_impl( std::ostream& os, Object const& obj ) { std::visit( [&]( auto const& e ){ os << e; }, obj.base() ); return os; } std::ostream& operator<<( std::ostream& os, Object const& obj ) { return stream_impl( os, obj ); } } 。仍然可以在与add_implObjectOnly::operator+相同的名称空间中看到operator+

请参见Float。如果将Integer编辑为不支持here,则会得到编译时错误,而不是运行时错误。


1
投票

如果选择单一类型作为规范的“公共”类型,并提供从多态类型到该公共类型的转换,那么您可以将其用作总和的最终结果和中间结果。

对于您的示例类,Integer对象可用于表示其值:

<<

然后,您可以通过循环计算总和:

float

0
投票

如果您想要一个多态向量,我认为这是最好的选择。您可以使用智能指针struct Object{ operator float() = 0; };

使其变得更好。
© www.soinside.com 2019 - 2024. All rights reserved.