【发布时间】:2018-09-22 19:22:10
【问题描述】:
我了解多态性和泛型如何在其他编程语言(Java、C#、Typescript 等)中进行交互。然而,在 C++ 中,这感觉就像我想使用的模式失败了。
在这个例子中,我想要一个扩展Words 的Names 列表。我想将我的名字列表传递给一个接受单词列表的方法,但我不能。我可以用我的名字填充一个单词列表,但这会丢失类型信息,这意味着我不能调用任何继承到 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++ equivalent of using <T extends Class> for a java parameter/return type)
对我来说,这意味着以下其中一项是正确的:
- 这种模式本质上是不可取的,不应该追求。
- 我认为丑陋的解决方案实际上是最好的解决方案和/或
- 我错误地访问它,因为它很丑 还有另一种解决方案 创建此模式
【问题讨论】:
-
查看CRTP 并确定是否可以解决您的问题。
-
嗨!这解决了我的直接问题。然而,在我的实现 (pastebin.com/9m5c0xtH) 中,我无法弄清楚如何创建
words的列表。我只能创建Names列表,然后将其传递给请求“扩展单词的事物”列表的方法。这是我需要的 85%,我很感激,但如果我错过了获得完美解决方案的方法,我会很好奇 -
为什么不只是模板
say_all()函数,像这样?say_all(std::list<T*> list) -
你想要类型的协变,C++ 不支持它,因为
std::list<X>与std::list<Y>没有任何关系。据我们所知,Y可能有专门化,使列表表现为队列。没有,但编译器不知道。std::vector<bool>有一个不幸的例子。协方差可以用转换运算符实现,但std::list没有。 -
另外我建议阅读堆栈、堆和智能指针。应该很少有你应该手写
new的例子。如果你写/看到一个,你应该总是很怀疑。
标签: c++ generics polymorphism