我了解多态和泛型如何在其他编程语言(Java,C#,Typescript等)中进行交互。但是在C ++中,感觉就像是我想利用的模式失败了。
在此示例中,我想要一个扩展Name
的{{1}}列表。我想将我的名字列表传递给一个接受单词列表的方法,但是我不能。我可以用我的姓名填充单词列表,但是会丢失类型信息,这意味着我无法调用继承到Name类的任何方法。
Word
例如,在Java中,我可以通过将所有人都定义为
来解决此问题#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
}
但是,在C ++中寻找这种解决方案会让我眼前一亮,就像一个丑陋的解决方案(C++ equivalent of using <T extends Class> for a java parameter/return type)
对我来说,这意味着下列条件之一是正确的:
答案 0 :(得分:0)
- 我错误地认为它很丑
那个。
我找不到以下丑陋的地方:
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
。答案 1 :(得分:0)
您应该能够使用::std::is_base_of
type trait实现与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;
}
答案 2 :(得分:0)
您没看错-C ++并不擅长此事。它目前没有等效于Java的有界类型参数,这意味着,如果您希望对say_all
可以采取什么措施进行特定级别的控制,而不是仅仅进行template<typename T> void say_all(list<T> const& l)
(甚至是{{1} }),并依靠内部用法抛出错误,则需要与template<typename T> void say_all(T const& l)
和朋友一起手动进行。
这是即将推出的C ++“概念”功能旨在解决的问题:
enable_if
(请注意,语法和标准库支持仍可能更改)。
不过,在这种情况下,如果您尝试传递其他列表,那么这只是在保证有保证,尽早且易于解决的编译器错误。诚实的说,我在这里的方法可能只是记录template<typename T> requires DerivedFrom<T, Word> void say_all(list<T> const& l) { ...
期望包含某个子类say_all
的列表,并且如果违反该列表,则依赖于可能的编译器错误。