如何处理c ++ 17中变体中包含的类型的无意义方法

问题描述 投票:1回答:1

我正在处理这样的情况,其中需要某个容器类来保存自定义类的变体(不仅是在向量中收集此类实例)。这些又相互关联。在代码示例中,此变量的类型为BirdFish,容器类为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_,并且使用aAnimalContainer,则代码应提供使用a.WingSpan()通过Bird类的实例depth_of_dive_请求a.DepthOfDive()的可能性。 ],则应为Fish。另外,对于每个BirdFish,可以估计((生理上不现实的)]权重,即可以调用a.EstimatedWeight()

基本上是为了避免编译器错误,将方法WingSpan()添加到Fish类,并将DepthOfDive()添加到Bird类。

添加这些虚拟方法可能变得非常麻烦,特别是当涉及两个以上的变体(此处为FishBird)或这些类包含许多方法时。

[一种可能性似乎会使特定类的访问者超载,并在所有其他情况下(再次使用通用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;
}

我正在处理这样的情况,其中需要某个容器类来保存自定义类的变体(不仅是在向量中收集此类实例)。这些依次与一个...

c++ variant visitor
1个回答
0
投票

首先,我很高兴看到新的贡献者对设计提出了一个结构良好的问题。欢迎使用StackOverflow! :)

正如您正确提到的,您有两种选择:处理具体类或容器中不存在的行为。让我们考虑这两种选择。

© www.soinside.com 2019 - 2024. All rights reserved.