C++ 泛型和多态性:这种模式可行吗?
C++ Generics and Polymorphism: is this pattern workable?
我了解多态性和泛型在其他编程语言(Java,C#,Typescript等(中的交互方式。然而,C++感觉就像我想利用的模式失败了。
在这个例子中,我想有一个扩展Word
s 的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
的列表,如果违反该错误,则依赖于可能的编译器错误。
- 错误处理.将系统错误代码映射到泛型
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 链表的泛型函数remove()与成员函数remove)
- 给定一个类型,如何派生一个泛型更广泛的类型(例如,用于溢出安全求和)?
- 模板化接口 - 创建一个泛型模板类以返回任何容器
- 如何编写将要继承的泛型代码?
- C++17 如何保存泛型可调用对象以供以后使用
- 使用宏扩展的泛型:为什么指令缓存使用不当?
- C++泛型类错误,问题出在哪里?
- C++泛型类,单独实现?
- 将参数传递给泛型 lambda 时复制构造函数不正确
- 泛型枚举和其他类型的重载模板函数
- 使用泛型类型推送到堆栈时出现问题
- 可变参数泛型 lambda 和函数重载
- C++ 泛型和多态性:这种模式可行吗?
- 这些语句是否等效(静态变量、常量变量和泛型)
- Java 是否像C++模板一样具有泛型推论?
- 为堆栈实现泛型集合
- 以特征类型作为参数的泛型函数回调
- 如何在容器中指定模板化别名的泛型类型