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";
это реализует таблицу диспетчеризации (более поздние компиляторы сгенерируют гораздо более эффективную), которая будет искать add
перегрузки.
Тип возвращаемого значения - Object
, но он будет содержать либо Float
или Integer
во время выполнения.
Список поддерживаемых вами типов должен находиться в одном месте по определению Object
. Эти объекты не обязательно должны быть связанными типами.
Вы можете расширить add_impl
в пространстве имен типов в Object
вместо центрального расположения. ADL будет использоваться для поиска набора перегрузки.
Конечно, Я бы реализовал 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_impl
от возможности видеть ObjectOnly::operator+
. Он по-прежнему сможет видеть operator+
в том же пространстве имен, что и Float
или Integer
.
См. здесь . Если вы отредактируете Integer
, чтобы не поддерживать <<
, вы получите время компиляции вместо ошибки времени выполнения.