views:

419

answers:

5

Hello. 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 "no matching function" error.

Container<Foo> container ();

Bar * temp = new Bar(container);

What is wrong? Are not templates polymorphic?

+3  A: 

No. Imagine that the container parameter is "hardcoded" into the class it defines (and that is actually how it works). Hence the container type is Container_Foo, that is not compatible with Container_Interface.

What you might try however is this:

template<class T>
Bar(const Container<T> & bar){
...
}

Yet you loose direct type checking that way.

Actually the STL way (probably more effective and generic) would be to do

template<class InputIterator>
Bar(InputIterator begin, InputIterator end){
...
}

... but I assume you don't have iterators implemented in the container.

Kornel Kisielewicz
That is very sad. Thanks for advice. I do not like that solution but I am afraid it is the only left.
Robin Hood
You assume right. I do not need them in that particular way. Frankly do not know how to implement them and do not have time to learn it right now.
Robin Hood
+6  A: 

There are two problems here: default constructions have the form MyClass c;; with parentheses it looks like a function declaration to the compiler.

The other problem is that Container<Interface> is simply a different type then Container<Foo> - you could do the following instead to actually get polymorphism:

Bar::Bar(const Container<Interface*>&) {}

Container<Interface*> container;
container.push_back(new Foo);
Bar* temp = new Bar(container);

Or of course you could make Bar or its constructor a template as Kornel has shown.

If you actually want some type-safe compile-time polymorphism, you could use Boost.TypeTraits is_base_of or some equivalent:

template<class T>
Bar::Bar(const Container<T>& c) {
    BOOST_STATIC_ASSERT((boost::is_base_of<Interface, T>::value));
    // ... will give a compile time error if T doesn't 
    // inherit from Interface
}
Georg Fritzsche
Thanks. I will try it.
Robin Hood
+1: come to think of it this would be a way nicer solution.
Kornel Kisielewicz
That is really nice and exactly what I need. I will not have to change a lot of already implemented code. Thanks again.
Robin Hood
"got the have the form". :P
GMan
I'd use BOOST_MPL_ASSERT_MSG instead of BOOST_STATIC_ASSERT: `BOOST_MPL_ASSERT_MSG( (boost::is_base_of<Interface, T>::value), T_MUST_BE_A_SUBCLASS_OF_INTERFACE___INSTEAD_T_IS, (T) );` If you used Foo2 instead of Foo the error should look like: `error: no matching function for call to ‘assertion_failed(mpl_::failed************ (T_MUST_BE_A_SUBCLASS_OF_INTERFACE___INSTEAD_T_IS::************)(Foo2))’`
KitsuneYMG
Thanks GMan - i need syntax checking for natural languages.
Georg Fritzsche
*Error - Line 1: Possessive followed by noun was followed by possessive - expected explanatory statement.*
GMan
Great, where can i download that? ;)
Georg Fritzsche
A: 

container is a container of Foo objects not a container of Interface objects

And it cannot be polymorphic either, pointers to things can be ,but not the objects themselvs. How big would the slots in the container have to be for container if you could put anything derived from interface in it

you need

 container<Interface*>

or better

 container<shared_ptr<Interface> >
pm100
@pm100, shared_ptr< Interface* >? Have you ever used a shared_ptr??
Kornel Kisielewicz
@kornel might be a typo...
msiemeri
@msiemeri - true, but note that the rest of the answer assumes that a container<Interface> stores values, while it might actually store Interface*
Kornel Kisielewicz
yes all the time, corrected, can i get the downvote off - :-)
pm100
@pm100, container<T> might have a member field vector<T*>, in which case your comment isn't true.
Kornel Kisielewicz
@kornel - i agree, container might actually store T*, of even shared_ptr<T>. If so then my second para is redundant. Sicnce code not supplied I issumed it stored T
pm100
@pm100, can't vote to old, so you need to edit it ;>
Kornel Kisielewicz
+2  A: 

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.

Luc Touraille
+1  A: 

It is possible to create an inheritance tree for containers, reflecting the inheritance tree of the data. If you have the following data:

class Interface {
public:
    virtual ~Interface()
        {}
    virtual void print() = 0;
};

class Number : public Interface {
public:
    Number(int value) : x( value )
        {}
    int get() const
        { return x; }
    void print()
        { std::printf( "%d\n", get() ); };
private:
    int x;
};

class String : public Interface {
public:
    String(const std::string & value) : x( value )
        {}
    const std::string &get() const
        { return x; }
    void print()
        { std::printf( "%s\n", get().c_str() ); }
private:
    std::string x;
};

You could also have the following containers:

class GenericContainer {
public:
    GenericContainer()
        {}
    ~GenericContainer()
        { v.clear(); }

    virtual void add(Interface &obj)
        { v.push_back( &obj ); }
    Interface &get(unsigned int i)
        { return *v[ i ]; }
    unsigned int size() const
        { return v.size(); }
private:
    std::vector<Interface *> v;
};

class NumericContainer : public GenericContainer {
public:
    virtual void add(Number &obj)
        { GenericContainer::add( obj ); }
    Number &get(unsigned int i)
        { return (Number &) GenericContainer::get( i ); }
};

class TextContainer : public GenericContainer {
public:
    virtual void add(String &obj)
        { GenericContainer::add( obj ); }
    String &get(unsigned int i)
        { return (String &) GenericContainer::get( i ); }
};

This is not the best performing code; it is just to give an idea. The only problem with this approach is that every time you add a new Data class, you have to also create a new Container. Apart from that, you have polymorphism "working again". You can be specific or general:

void print(GenericContainer & x)
{
    for(unsigned int i = 0; i < x.size(); ++i) {
        x.get( i ).print();
    }
}

void printNumbers(NumericContainer & x)
{
    for(unsigned int i = 0; i < x.size(); ++i) {
        printf( "Number: " );
        x.get( i ).print();
    }
}

int main()
{
    TextContainer strContainer;
    NumericContainer numContainer;
    Number n( 345 );
    String s( "Hello" );

    numContainer.add( n );
    strContainer.add( s );

    print( strContainer );
    print( numContainer );
    printNumbers( numContainer );
}
Baltasarq