传入通用模板化子类型

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

假设我有一个模板化的抽象

Fish
类,其中有两个模板化的子级:

template<typename T>
class Fish {
public:
    virtual ~Fish() = default;

    // Pure virtual method to make Fish an abstract class
    virtual void swim() const = 0;

    // A common method for all fish
    void breathe() const {
        std::cout << "Fish is breathing." << std::endl;
    }
};

// Templated Salmon class (child of Fish)
template<typename T>
class Salmon : public Fish<T> {
public:
    void swim() const override {
        std::cout << "Salmon is swimming with value = " << T << std::endl;
    }
};

// Templated Goldfish class (child of Fish)
template<typename T>
class Goldfish : public Fish<T> {
public:
    void swim() const override {
        std::cout << "Goldfish is swimming with value = " << T << std::endl;
    }
};

我想制作一个非模板化的 Aquarium 类,它存储 Fish 对象的向量并具有

void addFish(Fish)
Fish removeFish()
函数。

我该怎么做?

  1. 如何储存鱼?
  2. 我应该将什么作为 AddFish/RemoveFish 函数的参数/返回值?

类似于这个占位符代码

// Aquarium class (non-templated)
class Aquarium {
private:
    SomeType?? fishTank; // Store fish objects

public:
    // Add fish to the aquarium (now templated to handle any type of Fish<T>)
    template<typename T>
    void addFish(SomeType??? fish){...}

    template<typename T>
    SomeType??? removeFish(){...}

    // Method to make all fish swim
    void makeFishSwim() {
        for (auto& fish : fishTank) {
            fish->swim();
        }
    }
};

int main() {
    Aquarium aquarium;

    // Create fish objects
    auto salmon = std::make_shared<Salmon<int>>();
    auto goldfish = std::make_shared<Goldfish<int>>();

    // Add fish to the aquarium doing something like this
    aquarium.addFish(salmon);
    aquarium.addFish(goldfish);

    // Make all fish swim
    aquarium.makeFishSwim();

    return 0;
}
c++ templates inheritance
1个回答
0
投票

我认为这个例子涵盖了您正在寻找的内容:

  • 使用抽象基类来模拟 Fish 可以做什么,接口:FishItf
  • 你可以用它们制作一个向量
  • 用于可重用实现的 CRTP 类(因此您不需要进一步的通用 Fish 基类),称为 FishItfImpl。这种模式非常适合避免以后的钻石继承问题
  • 使用 typeid 从模板化类型中获取有点可用的字符串

参见:https://onlinegdb.com/mzbURVQVV

#include <iostream>
#include <memory>
#include <string>
#include <vector>

// Begin with an abstract baseclass modeling
// what a fish can do
class FishItf
{
public:
    virtual void Swim() = 0;
    virtual ~FishItf() = default;
};


// Default reusable implmentation of the fish interface
template<typename FishType>
class FishItfImpl : public FishItf
{
public:
    void Swim() override
    {
        std::string name = typeid(FishType).name();
        auto pos = name.find_first_not_of("0123456789");
        name = name.substr(pos);

        // typeid allows you to get a unique name for the type (not quite the original class name)
        std::cout << name << " is swimming\n";
    }
};

// A salmon with default fish implementation
class Salmon :
    public FishItfImpl<Salmon>
{
};    

// And a bad ass brass can sing too
class BadAssBrass :
    public FishItfImpl<BadAssBrass>
{
public:
    void Sing()
    {
        std::cout << "Take me to the river...\n";
    }

};


int main()
{
    Salmon salmon;
    salmon.Swim();
    
    BadAssBrass brass;
    brass.Swim();
    brass.Sing();

    // But you can now also make a vector of FishItf
    std::vector<std::unique_ptr<FishItf>> fishes;
    fishes.emplace_back(std::make_unique<Salmon>());
    fishes.emplace_back(std::make_unique<BadAssBrass>());

    std::cout << "\n\nLet all the fishes swim : \n";
    
    for(const auto& fish : fishes)
    {
        fish->Swim();
    }
};
© www.soinside.com 2019 - 2024. All rights reserved.