Перегрузки операторов в иерархии классов - PullRequest
0 голосов
/ 27 мая 2020

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

Я пытаюсь реализовать простую иерархию классов с определенными перегрузками операторов внутри базового класса (поскольку они не будут различаться между различными производными классами). Однако, поскольку большинству из них необходимо будет вернуть новый объект любого производного класса, в контексте которого мы находимся, я предполагаю, что они должны быть объявлены как шаблонные методы, однако, когда я пытаюсь скомпилировать это, компоновщик выдает ошибку «Неразрешенный внешний символ» ...

В качестве примера:

/// This is in the class header file
class Base
{
   // Declare all the base storage and methods ...

public:
   template<class T>
   friend T operator+(const T& lhs, const T& rhs);

   // .... other stuff in here.
}


/// Then in the implementation file
T operator+ (const T& lhs, const T& rhs)
{
   return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}

Я надеялся, что это приведет к тому, что я смогу объявить производные объекты таким образом :

class Derived : public Base
{
   // ... Add the derived class functionality

   // -- Question:  Do I need to do anything in here with operator+ ???
}
Derived A();
Derived B();

Derived C = A + B;

Это желаемый результат, но, не считая определения операторов в каждом отдельном производном классе, я не вижу способа реализовать это на C ++, поскольку подход шаблона приводит к ошибке компоновщика .

Я упускаю что-то совершенно очевидное и фундаментальное, или просто нет простого способа сделать это в C ++?

1 Ответ

2 голосов
/ 27 мая 2020

В вашем комментарии указано «в файле реализации» для шаблона, что, вероятно, является причиной вашей проблемы. Объявления шаблонов функций (например, T operator+(const T&, const&); объявляют символы, которые должны быть связаны, но для этого требуется создание этого шаблона где-то.

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

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


Тем не менее ...

Возможно, вы захотите пересмотреть свой текущий подход. Ваш шаблон operator+ не ограничивает тип T можно рассматривать, что приведет к тому, что operator+(const T&, const T&) будет жизнеспособной перегрузкой для любого * Тип 1022 * T, который еще не имеет operator+, при условии, что объявление отображается во время разрешения перегрузки. Это может привести к другим странным ошибкам компилятора / компоновщика.

Существует несколько способов решения проблемы ограничения типа; вероятно, самым простым было бы использовать SFINAE, чтобы ограничить его, проверив, что T является производным от Base.

Например:

/// This is in the class header file
class Base
{
   // Declare all the base storage and methods ...

public:
   template<class T, class>
   friend T operator+(const T& lhs, const T& rhs);

   // .... other stuff in here.
}


// Note: In the same header!
// This only enables + if 'T' derives from 'Base'
template <typename T, typename = std::enable_if_t<std::is_base_of<Base,T>::value>>
T operator+(const T& lhs, const T& rhs)
{
   return T(lhs->m_1 + rhs->m_1, lhs->m_2);
}
...