java - 在 C++ 中,是否有与 Java <? extends ClassName> 等价的?

  显示原文与译文双语对照的内容
79 4

我看这个 https://docs.oracle.com/javase/tutorial/java/generics/subtyping.htmlhttps://docs.oracle.com/javase/tutorial/java/generics/inheritance.html,问问自己如何在 C++ 中实现它。

我有一个小例子来说明:

#include <iostream>
class Animal
{
public:
 virtual std::string type() const = 0;
 virtual ~Animal() {}
};
class Dog : public Animal
{
public:
 virtual std::string type() const {
 return"I am a dog";
 }
};
class Cat : public Animal
{
public:
 virtual std::string type() const {
 return"I am a cat";
 }
};
template <typename T>
class AnimalFarm
{
};
void farmTest(const AnimalFarm<Animal *> &farm)
{ 
 std::cout <<"test farm";
}
int main(int argc, char *argv[])
{ 
 AnimalFarm<Dog *> dogFarm;
 AnimalFarm<Animal *> animalFarm;
 farmTest(animalFarm);//OK
 farmTest(dogFarm);//NOK compiler error as class AnimalFarm<Dog *> does not inherits from class AnimalFarm<Animal *>
 return 0;
}

我明白为什么它不能在 C++ 中工作。在Java中,解决方案是使用以下构造:

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number> numList = intList;//OK. List<? extends Integer> is a subtype of List<? extends Number>

( 给定 Integer 是示例链接中指出的Number的子类) 。

使用:

template <typename U>
void farmTest(const AnimalFarm<U *> &farm);

可以能是解决方案,但是在 C++ 中不会有更好的方法,而且 cat 或者狗会继承动物( 因为 IntegerNumber的子类型) 。

谢谢你。

时间:原作者:0个回答

139 3

end的游戏,是 AnimalFarm<Dog *>AnimalFarm<Animal *> 之间的一对一关系,那么一个带有类型特征的模板专门化就能做到这一点。

template <typename T, typename = void>
class AnimalFarm//Primary template
{
};
template<typename T>
class AnimalFarm<T*,
 std::enable_if_t<
!std::is_same<T, Animal>::value &&
 std::is_base_of<Animal, T>::value
> 
>//Specialization only instantiated when both conditions hold
//Otherwise SFINAE
 : public AnimalFarm<Animal*>
{
};

由于 AnimalFarm<Animal*> 成为 AnimalFarm<Dog*>的public 基,所以函数参数引用将绑定到它。尽管你应该注意到相应的层次结构是扁平的,不管 Animal的深度如何。

你可以在 live 查看它。

原作者:
115 1

等价性可能与我们定义等价( 如果不是意见)的方式有关。

如果你想为任何种类的动物打开一个运行时的容器,你会有

听起来像是一个很好的运行时多态性的解决方案。

如果不等价( 至少在我看来),运行时多态解决方案在功能上类似于你所要求的。我对运行时多态性的最佳描述是 "更好的父代码:运行时多态" 。

尽管这个思想将隐藏 Animal 继承层次结构,但从容器的角度来看,你只能使用任何容器。using AnimalFarm = std::vector<Animal>然后,详细信息将进入 Animal的实现,如下所示:

class Animal {
 struct Concept {
 virtual ~Concept() = default;
 virtual std::string type() const = 0;
//add in more methods for any other properties your Animal"concept" has
 };
 template <typename T>
 struct Model final: Concept {
 Model(T arg): data{std::move(arg)} {} 
 std::string type() const override {
 return get_type_name(data);
 }
//override define whatever other methods the concept has
 T data;//stores the underlying type's data
 };
 std::shared_ptr<const Concept> m_self;
public:
 template <typename T>
 Animal(T v): m_self{std::make_shared<Model<T>>(std::move(v))} {}
//default or delete other constructors as needed.
 friend std::string get_type_name(const Animal& animal) {
 return animal.m_self->type();
 }
};

现在要有一个像 Dog 这样的Animal,你只需要下面的免费代码:

struct Dog {
};
inline std::string get_type_name(const Dog& dog) {
 return"Dog";
}

使用( 假设至少 C++11 ) 就像。

using AnimalFarm = std::vector<Animal>;
int main() {
 auto theFarm = AnimalFarm{};
 theFarm.push_back(Dog{});
 for (const auto& e: theFarm) {
 std::cout <<get_type_name(e) <<"n";
 }
 return 0;
}

注意,在这里,Dog 被实例化,然后隐式地转换成一个 Animal,因为构造函数初始化的方式是定义构造函数的。

我只是手工输入了这段代码。所以它可能会有一些错误。希望它能在代码中显示这个答案的jist,我希望它对你的需要有帮助。

如果你想要容器只有一个特定种类的动物由的用户定义,那么。

然后只将种类的动物添加到上面的样式容器中。

如果希望编译器执行,则为或者 lastly 。

然后做一些类似的事情:

template <typename T>
using AnimalFarm = std::vector<T>;
void someCodeBlock() {
 AnimalFarm<Dog> dogFarm;
 dogFarm.push_back(Dog{});
}
原作者:
...