如何在基类指针向量的元素上应用重载的多态函数 [英] How to apply overloaded polymorphed function on elements of base class pointer vector
问题描述
我有一个基类Object
:
struct Object{
};
从此继承的
和n
(在本例中为2)类
and n
(in this case 2) classes that inherit from 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:
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.
我能想到...
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
强制转换为所有可用类型-两次,如果我有足够的子类,这似乎是一场灾难.
But this would require to dynamically cast Object
to all available types - twice, which seems like a catastrophe if I have enough children classes.
2)...模板,但是不起作用,因为类型在编译时未知.
2) ... Templates, but that won't work, because the types are not known at compile time.
那么,满足以下要求的最有效方法是:
*执行时间比内存使用更重要(尽管它应该在8GB的系统上运行)
*Execution time is more important than memory usage (although it should run on an 8GB system)
*它应该支持任意数量的子类,但必须至少达到20个
*It should support an arbitrary number of child classes, but must at least up to 20
*不仅限于添加,还应支持任意功能f(Object* a, Object* b)
*Is not limited to adding, but an arbitrary function f(Object* a, Object* b)
should be supported
*类的设计尚未确定.如果某些事情需要进行更改(或自行更改其总体结构),则可以
*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
*所有可能的类型都是预先已知的,不需要支持外部DLL
*All possible types are known upfront, external DLLs do not need to be supported
*不需要支持多重继承
*Does not need to support multiple inheritance
*在错误处理方面不需要鲁棒.可恢复会很好,但是我可以忍受SEGFAULT.
*Does not need to be robust in error handling. Recoverable would be nice but I can live with a SEGFAULT.
推荐答案
using Object = std::variant<Float, Integer>;
现在您可以拥有一个std::vector<Object>
并在其中存储Float
和Integer
.
now you can have a std::vector<Object>
and store Float
s and Integer
s in it.
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 );
}
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
重载.
this implements a dispatch table (more recent compilers will generate a far more efficient one) that will look for add
overloads.
返回类型为Object
,但在运行时将包含Float
或Integer
.
The return type is an Object
but it will contain either a Float
or an Integer
at runtime.
您需要支持的类型列表必须在Object
的定义处.这些对象不必是相关类型.
The list of types you support needs to be at one spot, at the definition of Object
. These objects don't have to be related types.
您可以在Object
中的类型的名称空间中扩展add_impl
,而不是在中心位置. ADL将用于查找过载集.
You can extend the add_impl
in the namespace of the types in Object
instead of in a central location. ADL will be used to find the overload set.
Of course, I'd implement operator+
instead of 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 );
}
}
这将阻止add_impl
看到ObjectOnly::operator+
.仍然可以在与Float
或Integer
相同的名称空间中看到operator+
.
this will block add_impl
from being able to see ObjectOnly::operator+
. It will still be able to see operator+
in the same namespace as Float
or Integer
.
请参见此处.如果将Integer
编辑为不支持<<
,则会得到编译时错误,而不是运行时错误.
See here. If you edit Integer
to not support <<
you'll get a compile-time instead of run-time error.
这篇关于如何在基类指针向量的元素上应用重载的多态函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!