Is there a Java equivalent <? extends ClassName> in C ++?
I look at this https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html and https://docs.oracle.com/javase/tutorial/java/generics/inheritance.html and ask yourself how it can be implemented in C ++.
I have this little example to illustrate:
#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; } I understand why it does not work in C ++. In Java, the solution should use the following construction:
List<? extends Integer> intList = new ArrayList<>(); List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number> (given that Integer is a subclass of Number , as indicated in the link examples).
Using:
template <typename U> void farmTest(const AnimalFarm<U *> &farm); may be a solution, but is there a better way to do this in C ++ without losing the fact that Cat or Dog inherits from Animal (as Integer is a subtype of Number )?
Thanks.
If your final game is to form an is-a relationship between AnimalFarm<Dog *> and AnimalFarm<Animal *> , then this can be done by a little specialization of patterns with the type of traits.
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*> { }; Since AnimalFarm<Animal*> becomes the public base of AnimalFarm<Dog*> , a link to the function parameter is bound to it. Although you should note that the corresponding hierarchy is flat, no matter how deep the Animal .
You can check it live .
Equivalence may be exactly how we define equivalence (if not an opinion).
If you want an open container for any kind of animal ...
Sounds like a good opportunity for run-time polymorphism.
A run-time polymorphic solution seems functionally similar if it is not equivalent (at least in my thinking) to what you are asking. The best description I know for run-time polymorphism that I mean is Sean Parent, "Best Code: Runtime Polymorphism . "
Basically, although the idea will hide the Animal inheritance hierarchy, so from the point of view of the container, you can simply use any container, for example using AnimalFarm = std::vector<Animal> . Details then proceed to the implementation of Animal as follows:
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 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(); } }; Now, to have Animal as a Dog , you just need a free code, for example:
struct Dog { }; inline std::string get_type_name(const Dog& dog) { return "Dog"; } And using (assuming at least C ++ 11) seems to be ...
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; } Note that here Dog is created ( Dog{} ) and then implicitly goes to Animal due to the fact that the Animal initialization constructor is defined (both template and non- explicit ).
I just typed this code manually. So he has some errors. Hope this shows the boldness of this answer in terms of code, although I hope this helps in your needs.
If, OTOH, you want the container to contain only a certain type of animal, defined by the user conatiner ...
Then add only instances of the animal species to the container with the specified style.
Or finally, if you want to use the compiler ...
Then do something like:
template <typename T> using AnimalFarm = std::vector<T>; void someCodeBlock() { AnimalFarm<Dog> dogFarm; dogFarm.push_back(Dog{}); } Constructive terminology
The second responder is StoryTellers and a suggestion to look at the relationship between types: the default relation in C ++ is invariance (i.e. the lack of a relation), for example. for function arguments, STL containers, and templates in general. The exceptions are smart pointers and return types of functions, which are all covariant.
Literature
If you use Google for the terms “variance”, “covariance” and “contravariance”, you will find some useful articles.
Effective C ++ and more efficient C ++ Scott Myers address the basics.
Sumant Tambe has a detailed article on his C ++ Truths blog post about covariance and contravariance in C ++ with an emphasis on the standard library . It contains a table listing the types of std libraries with (ntra), where the only type offering both is std::function<R *(T *)> with covariant return types and contravariant arguments. In addition, he shows various approaches to this relationship.
Best practice
In specification, design, and formal verification, a quasistandard is an invariant typicality and return types and arguments, as in std::function<R *(T *)> . However, there are other opinions, for example. Betrand Meyers' book Object-Oriented Software Engineering and its Eiffel language, which promotes covariant function arguments.
For C ++, I believe that universal versatility is most appropriate: because of the general specialization of patterns, covariance does not make any sense: you can completely specialize in AnimalFarm<Cat> so that it definitely does not correspond to the is-a relationship to AnimalFarm<Animal> . Due to static duck input, standard invariance in C ++ does not cause severe restrictions.