C++ 泛型和多态性:这种模式可行吗?

C++ Generics and Polymorphism: is this pattern workable?

本文关键字:模式 泛型 多态性 C++      更新时间:2023-10-16

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

在这个例子中,我想有一个扩展Words 的Name列表。我想将我的名字列表传递到一个接受单词列表的方法中,但我不能。我可以用我的名字填充单词列表,但这会丢失类型信息,这意味着我不能调用任何继承到 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中,我将能够通过将所有定义为

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

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

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

那。

我不觉得以下丑陋:

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>>std::make_unique

你应该能够使用::std::is_base_of类型特征实现类似于java的泛型函数:

template
<
typename x_Word
,   typename x_Enabled = ::std::enable_if_t
<
::std::is_base_of_v<Word, x_Word>
>
>
auto
say_all(::std::list<x_Word *> & words) -> void
{
for(auto & p_w: words)
{
p_w->say();
}    
return;
}

在线编译器

你没有错——这是C++不擅长的事情。它目前没有等同于 Java 的有界类型参数,这意味着如果你想对say_all可以采取什么进行特定级别的控制,而不仅仅是做template<typename T> void say_all(list<T> const& l)(甚至template<typename T> void say_all(T const& l)(并指望内部使用来抛出错误,你需要手动完成,与enable_if和朋友一起。

这是即将推出的C++"概念"功能旨在解决的问题:

template<typename T> requires DerivedFrom<T, Word> void say_all(list<T> const& l) { ...

(请注意,语法和标准库支持仍可能发生变化(。

尽管如此,在这种情况下,如果您尝试传递其他内容的列表,这只是为了保证、早期且易于解决的编译器错误。老实说,我在这里的方法可能是只记录say_all期望一个子类化Name的列表,如果违反该错误,则依赖于可能的编译器错误。