passing a polymorphic vectors

Okay this time I have an ACTUAL polymorphic problem.

1
2
3
4
5
void func(std::vector< BaseClass* > A){}

std::vector< SubClass* > B;

func(B); //Compile error C2664 


I get an error like so:
error C2664 'func' : cannot convert parameter from 'std::vector<_Ty>' to 'std::vector<_Ty>' with
[
    _Ty=B *
]
and
[
    _Ty=A *
]


I also tried some weird stuff like have the parameter be a pointer to the vector and I pass the address of the vector like so:

1
2
3
4
void func(std::vector< BaseClass* > *A){}

std::vector< SubClass* > B;
func(&B); //same error  


The error is summarized here: http://msdn.microsoft.com/en-us/library/s5b150wd(v=vs.71).aspx

I would appreciate any suggestions!
Last edited on
std::vector<BaseClass*> and std::vector<SubClass*> are DIFFERENT types, so you cannot do that. Remember: a SubClass object IS a BaseClass object; an std::vector<SubClass*>object IS NOT a std::vector<BaseClass*> object.

Try better:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class Container
{
private:
     std::vector<BaseClass*> data;
public:
     void Add(BaseClass*);
};

void func(Container&) {}

main()
{
     Container c;
     c.Add(new BaseClass);
     c.Add(new SubClass);
     func(c);
}
Last edited on
Containers aren't polymorphic.

Use a template function or use a vector of BaseClass pointers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <vector>

struct A 
{
    virtual void do_something() const { std::cout << "I'm an A!\n" ; }
} ;

class B : public A 
{
    void do_something() const { std::cout << "I'm a B!\n" ; }
} ;

void func( std::vector<A*>& a)
{
    for ( auto element : a )
        element->do_something() ;
}


int main()
{
    std::vector<A*> v ;

    v.push_back( new A ) ;
    v.push_back( new B ) ;

    func(v) ;
}
I'm an A!
I'm a B!


Please delete responsibly.
Last edited on
polymorphism comes in when you want BaseClass* to be the same as SubClass*, you can cast *SubClass to a *BaseClass. it will work if it acctually inherits from it, and you might need to use explicit casting.
Thank you for the replies everyone! cire, sadly I cannot change my std::vector<SubClass*> v;
to what you were doing in your main function std::vector<BaseClass*> v;

Right now I'm thinking I could run through a loop for(int i=0; i<v.size(); i++) and just push_back each SubClass* vector element to the BaseClass* vector, but I am looking for a more elegant solution because
1) better code readability, and
2) I like to learn advanced C++ tricks etc.

PS: the push_back thing compiled fine
dem7w2 wrote:
sadly I cannot change my std::vector<SubClass*> v;
to what you were doing in your main function std::vector<BaseClass*> v;
Why not? Who is imposing this restriction?
Last edited on
dem7w2 wrote:
I cannot change my...


Then go the template route.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <vector>

struct A 
{
    virtual void do_something() const { std::cout << "I'm an A!\n" ; }
} ;

struct B : public A 
{
    void do_something() const { std::cout << "I'm a B!\n" ; }
} ;

struct C: public B
{
    void do_something() const { std::cout << "I'm a C!\n" ; }
};

template <typename container>
void func( container& a)
{
    for ( auto element : a )
        element->do_something() ;
}


int main()
{
    std::vector<A*> v ;
    std::vector<B*> u ;

    v.push_back( new A ) ;
    v.push_back( new B ) ;
    v.push_back( new C ) ;

    u.push_back( new B ) ;
    u.push_back( new C ) ;

    func(v) ;
    std::cout << '\n' ;
    func(u) ;
}
Last edited on
L B, it'd involve changing a lot of code that is best not to touch.

Cire, once again, thanks for the tremendous help. Your solution isn't one I've considered and that sounds like it'd work great.
Cire, please, we have any oatmeal left?
TinyTeeTree wrote:
Cire, please, we have any oatmeal left?
I ate the rest of it for breakfast, I have exams in school today.
> Right now I'm thinking I could run through a loop for(int i=0; i<v.size(); i++)
> and just push_back each SubClass* vector element to the BaseClass*
> vector, but I am looking for a more elegant solution because
> 1) better code readability, and
> 2) I like to learn advanced C++ tricks etc.

There are a few C++ 'tricks' involved in this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <type_traits>
#include <memory>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <vector>
#include <list>

template < typename ALLOCATOR, typename T >
using rebind_to_type = typename std::allocator_traits<ALLOCATOR>::template rebind_alloc<T> ;

template < typename ALLOCATOR, typename FN, typename T >
using rebind_to_result_type = rebind_to_type< ALLOCATOR, typename std::result_of< FN(T) >::type > ;

template < typename U, typename T, typename A, template<typename,typename> class CONTAINER >
CONTAINER< U, rebind_to_type<A,U> > adapt( const CONTAINER<T,A>& c )
{   return CONTAINER< U, rebind_to_type<A,U> >( c.begin(), c.end() ) ; }

template < typename T, typename A, template<typename,typename> class CONTAINER, typename FN >
CONTAINER< typename std::result_of< FN(T) >::type, rebind_to_result_type<A,FN,T> >
adapt( const CONTAINER<T,A>& c, FN fn )
{
    CONTAINER< typename std::result_of< FN(T) >::type, rebind_to_result_type<A,FN,T> > result ;
    std::transform( c.begin(), c.end(), std::back_inserter(result), fn ) ;
    return result ;
}

int main()
{
    std::cout << std::fixed << std::showpoint << std::setprecision(2) ;

    std::vector<int> vec { 64, 74, 77, 87, 58, 58, 75, 87 } ;
    for( auto i : vec ) std::cout << i << ' ' ;
    std::cout << '\n' ;

    // implicit conversion int to double => std::vector<int> => std::vector<double>
    auto vec2 = adapt<double>(vec) ;
    for( auto d : vec2 ) std::cout << d << ' ' ;
    std::cout << '\n' ;

    std::list<short> lst( vec.rbegin(), vec.rend() ) ;
    for( auto s : lst ) std::cout << s << ' ' ;
    std::cout << '\n' ;

    // explicit transformation short to double => std::list<short> => std::list<double>
    auto lst2 = adapt( lst, [] ( int v ) { return v*v / 100.0 ; } ) ;
    for( auto d : lst2 ) std::cout << d << ' ' ;
    std::cout << '\n' ;

    struct base { int i ; }; struct offset { int i ; }; struct derived : offset, base {} ;
    std::vector<derived*> vec3 { new derived, new derived, new derived } ;
    for( auto p : vec3 ) std::cout << p << ' ' ;
    std::cout << '\n' ;

    // implicit conversion derived* to base* => std::vector<derived*> => std::vector<base*>
    std::vector<base*> vec4 = adapt<base*>(vec3) ;
    for( auto p : vec4 ) std::cout << p << ' ' ;
    std::cout << '\n' ;
}

http://ideone.com/4Lz8M2
Last edited on
If the types are implicitly convertible, you can use the constructor that takes a begin and end iterator.
> If the types are implicitly convertible, you can use the constructor that takes a begin and end iterator.

Yes. And if the types are not implicitly convertible, you can use a std::back_inserter along with std::transform() and a unary callable object.

Which is what these functions actually do:
return CONTAINER< U, rebind_to_type<A,U> >( c.begin(), c.end() ) ;

And std::transform( c.begin(), c.end(), std::back_inserter(result), fn ) ;

It can make the code a bit more succint when user-defined allocators are involved:
1
2
3
std::my_container< int, my_allocator<int,32,256> > a { /* .... */ };
std::my_container< double,  typename std::allocator_traits< my_allocator<int,32,256> >::template rebind_alloc<double> > b( a.begin(), a.end() ) ;
auto c = adapt<double>(a) ;


And it illustrates a few of C++ features: template aliases, std::result_of<>, std::allocator_traits<>.

Which are the two things that dem7w2 wanted.

This is intended to be a purely academic exercise. I suppose that special-casing for sequence containers (when polymorphic facilities to address the generality of sequences of any kind is available) is not the canonical C++ way.
Topic archived. No new replies allowed.