简体   繁体   English

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

[英]How to apply overloaded polymorphed function on elements of base class pointer vector

I have a base class Object :我有一个基类Object

struct Object{
};

and n (in this case 2) classes that inherit from thisn (在本例中为 2)个继承自 this 的类

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

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

By (ab-)using polymorphism I can now store those two types in a vector:通过(ab-)使用多态性,我现在可以将这两种类型存储在一个向量中:

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

But now I would like to add all those values together.但现在我想将所有这些值加在一起。

I can think of...我能想到...

1) ...defining functions 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);

But this would require to dynamically cast Object to all available types - twice, which seems like a catastrophe if I have enough children classes.但这需要将Object动态转换为所有可用类型 - 两次,如果我有足够的子类,这似乎是一场灾难。

2) ... Templates, but that won't work, because the types are not known at compile time. 2) ...模板,但这不起作用,因为在编译时不知道类型。

So what is the most efficient way regarding the following requirements:那么关于以下要求最有效的方法是什么:

*Execution time is more important than memory usage (although it should run on an 8GB system) *执行时间比内存使用更重要(虽然它应该在 8GB 系统上运行)

*It should support an arbitrary number of child classes, but must at least up to 20 *它应该支持任意数量的子类,但必须至少达到 20

*Is not limited to adding, but an arbitrary function f(Object* a, Object* b) should be supported *不限于加法,应支持任意函数f(Object* a, Object* b)

*The design of the classes is not yet fixed. *课程的设计尚未确定。 If something works that requires change (or changing the total structure in it self) that is possible如果某些事情需要改变(或改变其自身的总体结构),那是可能的

*All possible types are known upfront, external DLLs do not need to be supported *所有可能的类型都是预先知道的,不需要支持外部DLL

*Does not need to support multiple inheritance *不需要支持多重继承

*Does not need to be robust in error handling. *不需要在错误处理方面保持稳健。 Recoverable would be nice but I can live with a SEGFAULT.可恢复会很好,但我可以忍受 SEGFAULT。

using Object = std::variant<Float, Integer>;

now you can have a std::vector<Object> and store Float s and Integer s in it.现在你可以有一个std::vector<Object>并在其中存储Float s 和Integer s。

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

this implements a dispatch table (more recent compilers will generate a far more efficient one) that will look for add overloads.这实现了一个调度表(更新的编译器将生成一个更有效的调度表),它将寻找add重载。

The return type is an Object but it will contain either a Float or an Integer at runtime.返回类型是一个Object但它在运行时将包含一个Float或一个Integer

The list of types you support needs to be at one spot, at the definition of Object .您支持的类型列表需要在一个位置,在Object的定义处。 These objects don't have to be related types.这些对象不必是相关类型。

You can extend the add_impl in the namespace of the types in Object instead of in a central location.您可以在Object类型的命名空间中而不是在中央位置扩展add_impl ADL will be used to find the overload set. ADL 将用于查找重载集。

Of course, I'd implement operator+ instead of add .当然,我会实现operator+而不是add

There are some tricks you can use to fix:您可以使用一些技巧来修复:

// note: if the type in Object doesn't have a << overload,
// this will recurse and segfault.

that problem;那个问题; basically something like:基本上是这样的:

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

this will block add_impl from being able to see ObjectOnly::operator+ .这将阻止add_impl能够看到ObjectOnly::operator+ It will still be able to see operator+ in the same namespace as Float or Integer .它仍然可以在与FloatInteger相同的命名空间中看到operator+

See here .这里 If you edit Integer to not support << you'll get a compile-time instead of run-time error.如果您编辑Integer以不支持<<您将获得编译时而不是运行时错误。

If you can choose a single type as the canonical "common" type, and provide a conversion from polymorphic types to that common type, then you can use that as the final and intermediary result of the sum.如果您可以选择单一类型作为规范的“公共”类型,并提供从多态类型到该公共类型的转换,那么您可以将其用作总和的最终和中间结果。

For your example classes, a float object could be used to represent their value:对于您的示例类,可以使用float对象来表示它们的值:

struct Object{
    operator float() = 0;
};

Then, you can calculate the sum with a loop:然后,您可以使用循环计算总和:

float sum = 0;
for (Object* o : object_list) {
    sum += *o;
}

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

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