我正在处理这样的情况,其中需要某个容器类来保存自定义类的变体(不仅是在向量中收集此类实例)。这些又相互关联。在代码示例中,此变量的类型为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
,可以估计((生理上不现实的)]权重,即可以调用a.EstimatedWeight()
。基本上是为了避免编译器错误,将方法WingSpan()
添加到Fish类,并将DepthOfDive()
添加到Bird类。
添加这些虚拟方法可能变得非常麻烦,特别是当涉及两个以上的变体(此处为Fish
和Bird
)或这些类包含许多方法时。
[一种可能性似乎会使特定类的访问者超载,并在所有其他情况下(再次使用通用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; }
我正在处理这样的情况,其中需要某个容器类来保存自定义类的变体(不仅是在向量中收集此类实例)。这些依次与一个...
首先,我很高兴看到新的贡献者对设计提出了一个结构良好的问题。欢迎使用StackOverflow! :)
正如您正确提到的,您有两种选择:处理具体类或容器中不存在的行为。让我们考虑这两种选择。