При разработке решения иногда бывает удобно предоставить классы-обертки для примитивных типов данных. Рассмотрим класс, который представляет числовое значение, будь то double
, float
или int
.
class Number {
private:
double val;
public:
Number(int n) : val(n) { }
Number(float n) : val(n) { }
Number(double n) : val(n) { }
// Assume copy constructors and assignment operators exist
Number& add(const Number& other) {
val += other.val;
return *this;
}
int to_int() const { return (int) val; }
float to_float() const { return (float) val; }
double to_double() const { return val; }
};
Теперь предположим, что у меня есть функция как таковая:
void advanced_increment(Number& n) {
n.add(1);
}
И я бы использовал эту функцию как таковую:
Number n(2);
advanced_increment(n); // n = 3
Это звучит достаточно просто. Но что, если функция была такой?
void primitive_increment(int& n) {
++n;
}
Обратите внимание, что приращение является примером. Предполагается, что функция будет выполнять более сложные операции с примитивными типами данных, которые они также могут выполнять с Number
типами без каких-либо проблем.
Как бы я использовал функцию точно так же, как раньше? Как в:
Number n(2);
primitive_increment(n);
Как я могу сделать мой Number
класс совместимым с primitive_increment
? Как я могу создать класс-оболочку для примитивных типов данных, которые были бы совместимы везде, где требуются эти типы данных?
Пока что я нашел только два решения. Одним из них является создание такой функции, как double& Number::get_value()
, а затем использовать ее как primitive_increment(n.get_value());
. Второе решение заключается в создании методов неявного преобразования, таких как Number::operator int&()
; но это может привести ко многим неоднозначным вызовам и привести к путанице в коде.
Мне интересно, есть ли какое-либо другое решение для реализации этих типов оболочек и сохранения их примитивной функциональности.
Обновление:
Для дальнейшего уточнения, в настоящем проекте цель состоит в том, чтобы сделать все типы данных производными от одного базового класса, который обычно упоминается как Object
при разработке такого решения. Ограничение состоит в том, что никакая внешняя библиотека не должна использоваться. Поэтому, если у меня есть контейнер с указателями на тип Object
, он должен иметь возможность содержать любое произвольное значение, примитивное или нет, и выполнять любую примитивную операцию, которая разрешена для Object
. Я надеюсь, что это объясняет это лучше.