Вопрос

Я работаю над двумя классами-оболочками, которые определяют реальные и сложные типы данных.Каждый класс определяет перегруженные конструкторы, а также четыре арифметических оператора +,-,*,/ и пять операторов присваивания =,+= и т. д.Чтобы избежать повторения кода, я подумал об использовании шаблонных функций, когда аргументы левой и правой части оператора имеют разные типы данных:

// real.h
class Real {
public:
  explicit Real(const double& argument) {...}
  explicit Real(int argument) {...}
  ...

  friend const operator*(const Real&; const Real&);
  template <class T> friend const Real operator*(const Real&, const T&);
  template <class T> friend const Real operator*(const T&, cont Real&);
  // Here, T is meant to be a template parameter for double and int

  // Repeat for all other arithmetic and assignment operators
};

// complex.h
class Complex {
public:
  explicit Complex(const Real& realPart) {...}
  explicit Complex(const Real& realPart, const Real& imaginaryPart) {...}
  // Overload for double and int data types
  ...

  friend const operator*(const Complex&, const Complex&);
  template <class T> friend const Complex operator*(const Complex&, const T&);
  template <class T> friend const Complex operator*(const T&, cont Complex&);
  // Here, T is is a template parameter for Real, double and int

  ...
};

Проблема здесь в том, что код типа:

//main.cpp
void main() {
  Complex ac(2.0, 3.0);
  Real br(2.0);
  Complex cc = ac * br;
}

возвращает компилятор (GCC) ошибка неоднозначная перегрузка для 'operator*' в 'ac * br', поскольку компилятор не может отличить:

  • template <class T> friend const Complex operator*(const Complex&, const T&) [с Т = Реальный]
  • template <class T> friend const Real operator*(const T&, cont Real&) [с Т = Комплекс]

Есть ли способ указать, что T не может быть комплексом в определении шаблонного оператора* в классе Real?Или мне придется обойтись без шаблонов и определить каждый оператор для каждой возможной комбинации типов данных аргументов?Или есть способ перепроектировать код?

Это было полезно?

Решение

Ох, проблема операторов...

Boost создал прекрасную библиотеку, так что, предоставляя минимум логики, все остальные варианты автоматически добавляются за вас!

Взгляни на Boost.Операторы !

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

template <typename T>
Complex complex_mult_impl(T const& lhs, Complex const& rhs) { ... } // Note (1)

// return type is not 'Complex const', see (2)
Complex operator*(int lhs, Complex const& rhs)
{ 
  return complex_mult_impl(lhs,rhs);
}

Но если вы используете Boost.operators, вы предоставляете только Complex::operator*=(int) и Complex::operator*=(double), а автономные версии будут выведены автоматически :)

(1) Здесь вы можете использовать передачу по значению, если все аргументы являются встроенными.Вы также можете рассмотреть Boost.CallTraits, который автоматически выбирает между по значению и по ссылке в зависимости от того, встроен ли аргумент или нет.Это удобно для шаблонов.

(2) При возврате аргументов по значению бессмысленно квалифицировать их как constconst Ключевое слово означает что-то только для ссылок и указателей, здесь ничто не мешает пользователю создать «простой» Complex...и вам повезло, что это не так!

Другие советы

Вы можете сделать так, чтобы класс Real или Complex имел неглобальные операторы умножения.

class Real 
{
  ........

  template <class T> const Real operator*(const T&);
  const Real operator*(const Real&);

};

Можете ли вы сделать сложные конструкторы явными?Это будет означать, что неявное преобразование Real в Complex не разрешено и должно устранить неоднозначность оператора *

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top