Получите перегруженный оператор, но работайте только с теми же типами

Предположим, у меня есть базовый класс и два класса, производных от него:

class Base
{
protected:
double value;
public:
virtual ~Base();

Base(double value) : value(value) {}
Base(const Base& B) { value=B.value; }

Base operator+ (const Base& B) const {
return Base(value+B.value);
}

};

class final Derived1 : public Base {
public:
Derived1(double value) : Base(value) {}
};

class final Derived2 : public Base {
public:
Derived2(double value) : Base(value) {}
};

Я хочу сделать следующее:

int main(int argc, char *argv[])
{
Derived1 a = Derived1(4.0);
Derived2 b = Derived2(3.0);

a+a; // this should return a Derived1 object
b+b; // this should return a Derived2 object

a+b; // this should FAIL AT COMPILE TIME

return 0;
}

Другими словами, я хочу гарантировать, что наследственное operator+ работает только на объектах того же типа в качестве вызывающего экземпляра.

Как мне сделать это чисто? Я обнаружил, что переопределяю оператора для каждого класса:

class final Derived1 : public Base {
...
Derived1 operator+ (const Derived1& D1) const {
return Derived1(value+D1.value);
}
...
};

class final Derived2 : public Base {
...
Derived2 operator+ (const Derived2& D1) const {
return Derived2(value+D1.value);
}
...
};

Но это просто боль. Более того, это не похоже на правильное повторное использование кода для меня.

Какую технику использовать здесь?

0

Решение

Если вы можете убедиться Derived1 а также Derived2 являются листовыми классами (т.е. никакой другой класс не может быть производным от них), вы можете сделать это с помощью любопытно повторяющийся шаблон:

template <typename T>
class BaseWithAddition : public Base {
T operator+(T const& rhs) const {
return T(value + rhs.value);
}
};

class final Derived1 : public BaseWithAddition<Derived1> {
// blah blah
};

class final Derived2 : public BaseWithAddition<Derived2> {
// blah blah
};

(final это функция C ++ 11, которая предотвращает дальнейшее наследование.)

Если вы разрешите вывод из Derived1 а также Derived2 тогда у тебя проблемы

class Derived3 : public Derived1 {};
Derived3 d3;
Derived1 d1;
Derived1& d3_disguised = d3;
d1 + d3_disguised; // oooops, this is allowed

Нет способа предотвратить это во время компиляции. И даже если вы хотите разрешить это, нелегко получить приличную семантику для этой операции без многократная отправка.

5

Другие решения

Вы можете использовать специализированную функцию шаблона для добавления значений. К сожалению, этот трюк не работает с операторами:
Сбой, если типы не совпадают, и возвращает правильный тип:

#include <type_traits>
class Base;
template <class Derived>
Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);class Base
{
...
template <class Derived>
friend Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL);
};

template <class Derived>
Derived add(const Derived& l, const Derived& r,
typename std::enable_if<std::is_base_of<Base,Derived>::value>::type* = NULL)
{
return l.value + r.value;
}

И доказательство того, что это работает:

int main() {
int a = 0;
a = a + a;
Derived1 d11(0), d12(0);
Derived2 d21(0), d22(0);
add(d11, d12);
add(d21, d22);
add(d12, d22); // here it fails to compile...
}
1

Пока value определяется только в базовом классе, и для операции не требуется доступ к каким-либо производным элементам, возможно, вам удастся обойтись только определением базового оператора и разрешением неявного приведения типов обрабатывать все остальное. Что касается ошибок с разными типами, может быть, стоит пожертвовать небольшой суммой, используя систему на основе enum для отслеживания типов, а затем выполнить простое сравнение, чтобы проверить наличие недопустимых условий.

enum eTypeEnum {BASE, DER1, DER2};

class Base {
public:
virtual ~Base(){}

Base(double value) : eType(BASE),value(value) {}
Base(const Base& B) { value=B.value; }

Base operator+ (const Base& B) const {
if (eType != B.eType) return -1; //error condition
return Base(value+B.value);
}
double getVal(){return value;}
protected:
eTypeEnum eType;
double value;
};

class Derived1 : public Base {
public:
Derived1(double value) : Base(value) {eType = DER1;}
};

class Derived2 : public Base {
public:
Derived2(double value) : Base(value) {eType = DER2;}
};int main() {
int tmp;
Derived1 a(4.0);
Derived2 b(3.0);
Base c(2.0);

cout << "aa:" << (a+a).getVal();     // 8
cout << "\nbb:" << (b+b).getVal();   // 6
cout << "\nba:" << (b+a).getVal();   // 7
cout << "\nab:"<< (a+b).getVal();    // 7

cout << "\ncc:"<< (c+c).getVal();    // 4
cout << "\nac:"<< (a+c).getVal();    // 6
cout << "\nbc:" << (b+c).getVal();   // 5
cout << "\nabc:" << (a+b+c).getVal();// 9
cout << endl;
cin >> tmp;
return 0;
}

Выходы:
аа: 8
бб: 6
ба: -1
AB: -1
Копии: 4
переменный ток: -1
BC: -1
а: 1

Единственная проблема, которую я вижу, заключается в том, что при объединении нескольких операций литье приводит к повышению управляемости. Вот, a+b+c 432 оценивается как (a+b)+c Итак a+b бит испытывает состояние ошибки (возвращая -1), но преобразуется как Base который позволяет (-1)+c вернуть ‘1’.

-3
По вопросам рекламы [email protected]