Search code examples
c++templatespolymorphism

Templates polymorphism


I have this structure of classes.

class Interface {
  // ...
};

class Foo : public Interface {
  // ...
};

template <class T>
class Container {
  // ...
};

And I have this constructor of some other class Bar.

Bar(const Container<Interface> & bar){
  // ...
}

When I call the constructor this way I get a "no matching function" error.

Container<Foo> container ();

Bar * temp = new Bar(container);

What is wrong? Are templates not polymorphic?


Solution

  • I think the exact terminology for what you need is "template covariance", meaning that if B inherits from A, then somehow T<B> inherits from T<A>. This is not the case in C++, nor it is with Java and C# generics*.

    There is a good reason to avoid template covariance: this will simply remove all type safety in the template class. Let me explain with the following example:

    //Assume the following class hierarchy
    class Fruit {...};
    
    class Apple : public Fruit {...};
    
    class Orange : public Fruit {...};
    
    //Now I will use these types to instantiate a class template, namely std::vector
    int main()
    {
        std::vector<Apple> apple_vec;
        apple_vec.push_back(Apple()); //no problem here
    
        //If templates were covariant, the following would be legal
        std::vector<Fruit> & fruit_vec = apple_vec;
    
        //push_back would expect a Fruit, so I could pass it an Orange
        fruit_vec.push_back(Orange()); 
    
        //Oh no! I just added an orange in my apple basket!
    }
    

    Consequently, you should consider T<A> and T<B> as completely unrelated types, regardless of the relation between A and B.

    So how could you solve the issue you're facing? In Java and C#, you could use respectively bounded wildcards and constraints:

    //Java code
    Bar(Container<? extends Interface) {...}
    
    //C# code
    Bar<T>(Container<T> container) where T : Interface {...}
    

    The next C++ Standard (known as C++1x (formerly C++0x)) initially contained an even more powerful mechanism named Concepts, that would have let developers enforce syntaxic and/or semantic requirements on template parameters, but was unfortunately postponed to a later date. However, Boost has a Concept Check library that may interest you.

    Nevertheless, concepts might be a little overkill for the problem you encounter, an using a simple static assert as proposed by @gf is probably the best solution.

    * Update: Since .Net Framework 4, it is possible to mark generic parameters has being covariant or contravariant.