如何在基 class 指针向量的元素上应用重载的多态函数
How to apply overloaded polymorphed function on elements of base class pointer vector
我有一个基地classObject
:
struct Object{
};
和n
(在本例中为 2)class继承自此
struct Integer : public Object{
int i_;
Integer(int i) : i_{i}{}
}
struct Float : public Object{
float f_;
Float(float f) : f_{f}{}
}
通过(ab-)使用多态性,我现在可以将这两种类型存储在一个向量中:
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
动态转换为所有可用类型 - 两次,如果我有足够的 children classes,这似乎是一场灾难。
2) ... 模板,但这行不通,因为类型在编译时是未知的。
那么对于以下要求,最有效的方法是什么:
*执行时间比内存使用更重要(尽管在 8GB 系统上它应该 运行)
*它应该支持任意数量的 child classes,但至少必须达到 20
*不限于加法,应该支持任意函数f(Object* a, Object* b)
*classes 的设计尚未确定。如果某些东西需要改变(或改变它自己的整体结构),那是可能的
*预先知道所有可能的类型,不需要支持外部 DLL
*不需要支持多重继承
*在错误处理方面不需要很健壮。可恢复的会很好,但我可以忍受 SEGFAULT。
using Object = std::variant<Float, Integer>;
现在您可以拥有一个 std::vector<Object>
并在其中存储 Float
和 Integer
。
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";
这实现了一个分派 table(更新的编译器将生成一个更高效的分派),它将寻找 add
重载。
return 类型是 Object
,但它将在 运行 时包含 Float
或 Integer
。
您支持的类型列表需要位于一处,即 Object
的定义处。这些对象不必是相关类型。
您可以在 Object
类型的命名空间而不是在中央位置扩展 add_impl
。 ADL 将用于查找重载集。
当然I'd implement operator+
instead of 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_impl
看到 ObjectOnly::operator+
。它仍然可以在与 Float
或 Integer
.
相同的命名空间中看到 operator+
参见 here。如果您将 Integer
编辑为不支持 <<
,您将得到编译时错误而不是 运行 时错误。
如果您可以选择单一类型作为规范 "common" 类型,并提供从多态类型到该通用类型的转换,那么您可以将其用作求和的最终和中间结果。
对于您的示例 类,可以使用 float
对象来表示它们的值:
struct Object{
operator float() = 0;
};
然后,你可以循环计算总和:
float sum = 0;
for (Object* o : object_list) {
sum += *o;
}
我有一个基地classObject
:
struct Object{
};
和n
(在本例中为 2)class继承自此
struct Integer : public Object{
int i_;
Integer(int i) : i_{i}{}
}
struct Float : public Object{
float f_;
Float(float f) : f_{f}{}
}
通过(ab-)使用多态性,我现在可以将这两种类型存储在一个向量中:
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
动态转换为所有可用类型 - 两次,如果我有足够的 children classes,这似乎是一场灾难。
2) ... 模板,但这行不通,因为类型在编译时是未知的。
那么对于以下要求,最有效的方法是什么:
*执行时间比内存使用更重要(尽管在 8GB 系统上它应该 运行)
*它应该支持任意数量的 child classes,但至少必须达到 20
*不限于加法,应该支持任意函数f(Object* a, Object* b)
*classes 的设计尚未确定。如果某些东西需要改变(或改变它自己的整体结构),那是可能的
*预先知道所有可能的类型,不需要支持外部 DLL
*不需要支持多重继承
*在错误处理方面不需要很健壮。可恢复的会很好,但我可以忍受 SEGFAULT。
using Object = std::variant<Float, Integer>;
现在您可以拥有一个 std::vector<Object>
并在其中存储 Float
和 Integer
。
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";
这实现了一个分派 table(更新的编译器将生成一个更高效的分派),它将寻找 add
重载。
return 类型是 Object
,但它将在 运行 时包含 Float
或 Integer
。
您支持的类型列表需要位于一处,即 Object
的定义处。这些对象不必是相关类型。
您可以在 Object
类型的命名空间而不是在中央位置扩展 add_impl
。 ADL 将用于查找重载集。
当然I'd implement operator+
instead of 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_impl
看到 ObjectOnly::operator+
。它仍然可以在与 Float
或 Integer
.
operator+
参见 here。如果您将 Integer
编辑为不支持 <<
,您将得到编译时错误而不是 运行 时错误。
如果您可以选择单一类型作为规范 "common" 类型,并提供从多态类型到该通用类型的转换,那么您可以将其用作求和的最终和中间结果。
对于您的示例 类,可以使用 float
对象来表示它们的值:
struct Object{
operator float() = 0;
};
然后,你可以循环计算总和:
float sum = 0;
for (Object* o : object_list) {
sum += *o;
}