#c #operator-overloading #c 14
Вопрос:
У меня возникла проблема при переопределении оператора базового класса==. Вот код.
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class IClient {
virtual const std::vector<T>amp; getID() = 0;
virtual bool isEqual(const std::vector<T>amp; anotherID) = 0;
virtual bool operator==(const IClientamp; anotherClient) = 0;
};
class SeviceClient : public IClient<int8_t> {
const std::vector<int8_t> ID;
public:
SeviceClient(const std::vector<int8_t>amp; ID) : ID(std::move(ID)) {}
SeviceClient(const std::vector<int8_t>amp;amp; ID) : ID(std::move(ID)) {}
const std::vector<int8_t>amp; getID() override {
return ID;
}
bool isEqual(const std::vector<int8_t>amp; anotherID) override {
return true;
}
bool operator==(const SeviceClientamp; anotherClient) override {
return true;
}
};
Он жалуется, что
error: 'bool SeviceClient::operator==(const SeviceClientamp;)' marked 'override', but does not override
Компилятор выглядит счастливым, когда я преобразую функцию в
bool operator==(const IClient<int8_t>amp; anotherClient) override {}
Является ли это решением проблемы или я что-то здесь упускаю ?
Спасибо
Комментарии:
1. Попробуй
bool operator==(const IClientamp; anotherClient) override
.2. Привет @MarekR Почему ты думаешь, что это плохой дизайн ? 1. Я хотел бы иметь класс интерфейса с чисто виртуальными функциями, чтобы в будущем издеваться. 2. Во-вторых, у меня есть два Клиента
SeviceClient : public IClient<int8_t>
иclass SubsciberClient : public IClient<uint8_t>
. Я также могу написать один производный класс с шаблоном, а не два класса, как этотtemplate<typename T> Clinet : public IClient<T>
, но предпочитаю писать два разных производных класса. Есть какие-нибудь предложения по дизайну ?3. @BhanuKiran операторы равенства обычно определяются для классов структурных характеристик (без полиморфизма, без обратного вызова, вещей, которые можно скопировать). Я не знаю, какова природа вашей реальной проблемы, поэтому я просто предполагаю, так как мне это кажется неправильным.
Ответ №1:
SeviceClient и IClient-это разные типы. Итак, bool SeviceClient::оператор==(const SeviceClientamp;) не имеет базового объявления.
Ответ №2:
Вы должны понимать, что C имеет возможность перегружать функции. Таким образом, для идентификации функции, которую предполагается вызвать, должно совпадать не только имя функции, но и типы аргументов. Конечно, существует неявное преобразование, которое может адаптировать аргументы в соответствии с типами аргументов функций, но обратите внимание, что если существует несколько функций с одинаковыми именами, то неявное преобразование может привести к двусмысленности.
Если вы используете полиморфизм, то вы должны помнить, что дочерний класс должен использоваться, поскольку это был просто родительский класс. Тот, кто держит указатель на базовый класс, не должен замечать, что на самом деле это подкласс.
Поэтому при переопределении виртуальных функций типы аргументов должны быть очень точными, поэтому все однозначно.
Я подозреваю, что вместо динамического полиморфизма вам нужен статический полиморфизм и CRTP. Так что что-то вроде этого:
template <typename Child, typename T>
class IClient {
const std::vector<T>amp; getID() {
return asChild()->getID();
}
bool isEqual(const std::vector<T>amp; anotherID) {
return asChild()->isEqual(anotherID);
}
bool operator==(const Childamp; anotherClient) {
return asChild()->isEqual(anotherClient);
}
private:
Child* asChild() {
return static_cast<Child*>(this);
}
const Child* asChild() const {
return static_cast<const Child*>(this);
}
};
class SeviceClient : public IClient<SeviceClient, int8_t> {
const std::vector<int8_t> ID;
public:
SeviceClient(const std::vector<int8_t>amp; ID) : ID(std::move(ID)) {}
SeviceClient(const std::vector<int8_t>amp;amp; ID) : ID(std::move(ID)) {}
const std::vector<int8_t>amp; getID() {
return ID;
}
bool isEqual(const std::vector<int8_t>amp; anotherID) {
return true;
}
bool operator==(const SeviceClientamp; anotherClient) {
return true;
}
};
Комментарии:
1. Спасибо за код. После некоторого размышления и объяснения от другого участника переполнения стека. Они решили устранить перегрузку оператора. Тем не менее, спасибо за вклад.
Ответ №3:
У этого вопроса есть две стороны. Во-первых, какие правила C нарушает этот дизайн? И во-вторых, почему в C существуют такие правила?
Первый из них очень прост. Вы можете переопределить функцию функцией с точно таким же списком аргументов. Так (const IClientamp; anotherClient)
как это не совсем то же самое (const SeviceClientamp; anotherClient)
, переопределение не происходит. Дело закрыто.
Второй немного более вовлечен. Чтобы понять это, давайте воспользуемся более знакомой иерархией классов. Есть class Animal
, и есть class Dog : public Animal
, и class Cat : public Animal
есть .
Что bool Animal::operator==(const Animalamp;)
нам говорит? Это говорит нам о том, что мы можем сравнить животное с другим животным. В частности, мы можем сравнить собаку с собакой, и кошку с кошкой, а также собаку с кошкой, и крысу с летучей мышью, и рыбу с москито, и трубкозуба с тираннозавром. Это обещание, которое class Animal
дает нам ты. Если у нас есть два Animal
объекта, мы можем их сравнить.
Теперь функция, которая переопределяет этот оператор, должна выполнить обещание. bool Dog::operator==(const Dogamp;)
Неужели это так? Нет, это выполняет гораздо более узкое обещание: что это Animal
(то, что оказывается а Dog
) можно сравнить с а Dog
. Законно ли выполнять это узкое обещание? Абсолютно. Законно ли притворяться, что эта функция также выполняет широкие обещания-возможность сравнивать любых двух животных? Ни в коем случае. Таким образом, мы можем объявить эту функцию, но не можем утверждать, что она переопределяет оператор Animal
равенства.
Что бы произошло, если бы существовало правило, которое позволяло бы нам утверждать это?
Dog d;
Cat c;
Animalamp; da = d;
Animalamp; ca = c;
std::cout << (da == ca);
Этот фрагмент программы должен был бы быть скомпилирован, так как каждая строка допустима (в соответствии с нашим правилом фантазии), но что он должен делать? Нет кода, который сравнивал бы собаку с кошкой!
Некоторые языки говорят: «Хорошо, ничего страшного, мы просто выдадим ошибку во время выполнения». C этого не делает. Если вы хотите, чтобы приведенный выше код выдавал ошибку во время выполнения, вам следует написать код, который сам выдает ошибку во время выполнения. Это означает переопределение bool Animal::operator==(const Animalamp;)
с bool Dog::operator==(const Animalamp;)
помощью и bool Cat::operator==(const Animalamp;)
и проверку наличия там условия ошибки.
Хотя, вероятно, это не очень хорошая идея. Равенство обычно имеет мало смысла для иерархий классов. Обычно гораздо лучше либо вообще исключить сравнение, либо сделать его невиртуальным и определенным только в дочерних классах.
Комментарии:
1. Чем больше я думаю об этом, тем больше в этом смысла. Я решил удалить перегрузку оператора. Спасибо за объяснение.