首页 > 解决方案 > C++ 泛型和多态:这种模式可行吗?

问题描述

我了解多态性和泛型如何在其他编程语言(Java、C#、Typescript 等)中交互。然而,在 C++ 中,它感觉就像我想使用的模式失败了。

在这个例子中,我想要一个Name扩展Words 的 s 列表。我想将我的名字列表传递给一个接受单词列表的方法,但我不能。我可以用我的名字填充单词列表,但这会丢失类型信息,这意味着我不能调用任何继承到 Name 类的方法。

#include <iostream>
#include <string>
#include <list>

class Word{
    public:
        virtual void say() = 0;
};

class Name : public Word{
    std::string name;
    public:
        Name(std::string name){
            this-> name = name;
        }
        void say() override{
            std::cout << name << std::endl;
        }
        void important_name_function(){
           // Something very important I want to call
        }
};

void say_one(Word* w){
    w-> say();
}

void say_all(std::list<Word*> list){
    for(Word* w: list){
        w-> say();
    }    
}

int main(){
    std::list<Word*> words = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
    say_one(words.front()); //Works, due to the magic of polymorphism
    say_all(words); //Works, due to the magic of polymorphism

    std::list<Name*> names = {new Name("Kai"), new Name("Ben"), new Name("Sam")};
    say_one(names.front()); //STILL works due to the magic of polymorphism AND type information is retained
    say_all(names); //Fails but feels like it shouldn't
}

例如,在 Java 中,我可以通过将 say all 定义为

static <T extends Word> void say_all (java.util.LinkedList<T> list){
    for(T w:list){
        w.say();
    }  
}

但是,在 C++ 中寻找这个解决方案在我看来是一个丑陋的解决方案(C++ 相当于使用 <T extends Class> 作为 java 参数/返回类型

对我来说,这意味着以下其中一项是正确的:

标签: c++genericspolymorphism

解决方案


  • 我错误地将其评估为丑陋

那。

我不觉得以下丑陋:

template<class T>
void say_all(const std::list<T*>& list) {
    for (T* w : list) {
        w->say();
    }    
}

请注意,您根本不必T在示例中进行限制。在Java中无法真正匹配。

仅当您实际上需要限制T为以下实例时Word

template<class T, typename = std::enable_if_t<std::is_base_of<Word, T>::value>>
void say_all(const std::list<T*>& list) {
    for (T* w : list) {
        w->say();
    }    
}

或与概念:

template<typename T>
concept IsWord = std::is_base_of<Word, T>::value;

template<class T> requires IsWord<T>
void say_all(const std::list<T*>& list) {
    for(T* w : list) {
        w->say();
    }    
}

旁注:

  • 通过引用传递对象来避免不必要地复制对象。
  • 为了减少内存泄漏,请避免使用运算符new并使用std::list<std::unique_ptr<Word>>andstd::make_unique来代替。

推荐阅读