Я имею дело со случаем, когда определенный контейнерный класс должен содержать вариант пользовательских классов (не в последнюю очередь для сбора экземпляров таких классов в векторе). Они в свою очередь связаны друг с другом. В примере кода типы в этом варианте - Bird
и Fish
, а класс контейнера - AnimalContainer
(полный рабочий код см. Ниже).
Неполный класс обзор:
using namespace std;
using uint = unsigned int;
class Animal {
protected:
uint length_;
};
class Fish : public Animal {
private:
uint depths_of_dive_;
};
class Bird : public Animal {
private:
uint wing_span_;
};
class AnimalContainer {
private:
variant<Bird, Fish> the_animal_;
};
Теперь (игнорируя пингвинов и некоторых других птиц) , птицы обычно не могут нырять, а у рыб нет крыльев (не слышал любого хотя бы) . Однако код должен предоставлять возможность запрашивать wing_span_
через экземпляр a
класса AnimalContainer
с использованием a.WingSpan()
, если это животное будет Bird
, а также depth_of_dive_
с использованием a.DepthOfDive()
, это должно быть Fish
. Кроме того, для каждого Bird
и Fish
можно оценить (физиологически нереалистичный c) вес, то есть можно назвать a.EstimatedWeight()
.
В основном для того, чтобы Во избежание ошибок компилятора, метод WingSpan()
добавлен в класс Fi sh, а DepthOfDive()
добавлен в класс Bird.
Добавление этих фиктивных методов может стать очень громоздким, особенно когда более двух варианты (здесь Fish
и Bird
), или когда эти классы содержат много методов.
Одна возможность, кажется, перегружает посетителя для определенных c классов и возвращает некоторое предупреждение во всех других случаях (опять же, используя generic c lambda), но хотя это немного улучшает процесс, это также довольно громоздко (см. второй пример кода ниже).
У вас есть предложения, как это сделать в более всеобъемлющий способ, который требует меньше копирования и вставки? Если у вас есть общие проблемы с этой концепцией, советуем также приветствовать.
Кстати, класс контейнера животных позже помещается в другой класс, который ведет пользователя во избежание непреднамеренных вызовов фиктивных функции.
Пример первого рабочего кода
#include <variant>
#include <iostream>
using namespace std;
using uint = unsigned int;
class Animal {
public:
Animal(uint length) : length_{length} {}
uint Length() { return length_; }
protected:
uint length_;
};
class Fish : public Animal {
public:
Fish(uint length, uint depths_of_dive) : Animal(length), depths_of_dive_{depths_of_dive} {}
uint DepthOfDive() { return depths_of_dive_; }
uint EstimatedWeight() { return length_ * length_; }
uint WingSpan() { cerr << "Usually fishes do not have wings... "; return 0; }
private:
uint depths_of_dive_;
};
class Bird : public Animal {
public:
Bird(uint length, uint wing_span) : Animal(length), wing_span_{wing_span} {}
uint WingSpan() { return wing_span_; }
uint EstimatedWeight() { return wing_span_ * length_; }
uint DepthOfDive() { cerr << "Usually birds can not dive... "; return 0; }
private:
uint wing_span_;
};
class AnimalContainer {
public:
AnimalContainer(Bird b) : the_animal_{b} {}
AnimalContainer(Fish f) : the_animal_{f} {}
uint Length() {
return visit([] (auto arg) { return arg.Length(); }, the_animal_);
}
uint WingSpan() {
return visit([] (auto arg) { return arg.WingSpan(); }, the_animal_);
}
uint DepthOfDive() {
return visit([] (auto arg) { return arg.DepthOfDive(); }, the_animal_);
}
uint EstimatedWeight() {
return visit([] (auto arg) { return arg.EstimatedWeight(); }, the_animal_);
}
private:
variant<Bird, Fish> the_animal_;
};
int main()
{
Fish f(2,3);
Bird b(2,3);
AnimalContainer a_1(f);
AnimalContainer a_2(b);
cout << a_1.Length() << ' ' << a_1.WingSpan() << ' ' << a_1.DepthOfDive() << ' ' << a_1.EstimatedWeight() << endl;
cout << a_2.Length() << ' ' << a_2.WingSpan() << ' ' << a_2.DepthOfDive() << ' ' << a_2.EstimatedWeight() << endl;
return 0;
}
Пример второго рабочего кода
#include <variant>
#include <iostream>
using namespace std;
using uint = unsigned int;
class Animal {
public:
Animal(uint length) : length_{length} {}
uint Length() { return length_; }
protected:
uint length_;
};
class Fish : public Animal {
public:
Fish(uint length, uint depths_of_dive) : Animal(length), depths_of_dive_{depths_of_dive} {}
uint DepthOfDive() { return depths_of_dive_; }
uint EstimatedWeight() { return length_ * length_; }
// no more dummy function
private:
uint depths_of_dive_;
};
class Bird : public Animal {
public:
Bird(uint length, uint wing_span) : Animal(length), wing_span_{wing_span} {}
uint WingSpan() { return wing_span_; }
uint EstimatedWeight() { return wing_span_ * length_; }
// no more dummy function
private:
uint wing_span_;
};
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
class AnimalContainer {
public:
AnimalContainer(Bird b) : the_animal_{b} {}
AnimalContainer(Fish f) : the_animal_{f} {}
uint Length() {
return visit([] (auto arg) { return arg.Length(); }, the_animal_);
}
uint WingSpan() {
return visit(overloaded { // now overloaded version
[] (auto) { cerr << "This animal does not have wings... "; return uint(0); },
[] (Bird arg) { return arg.WingSpan(); }}, the_animal_);
}
uint DepthOfDive() {
return visit(overloaded { // now overloaded version
[] (auto) { cerr << "This animal can not dive... "; return uint(0); },
[] (Fish arg) { return arg.DepthOfDive(); }}, the_animal_);
}
uint EstimatedWeight() {
return visit([] (auto arg) { return arg.EstimatedWeight(); }, the_animal_);
}
private:
variant<Bird, Fish> the_animal_;
};
int main()
{
Fish f(2,3);
Bird b(2,3);
AnimalContainer a_1(f);
AnimalContainer a_2(b);
cout << a_1.Length() << ' ' << a_1.WingSpan() << ' ' << a_1.DepthOfDive() << ' ' << a_1.EstimatedWeight() << endl;
cout << a_2.Length() << ' ' << a_2.WingSpan() << ' ' << a_2.DepthOfDive() << ' ' << a_2.EstimatedWeight() << endl;
return 0;
}