views:

343

answers:

2
#include <vector>

struct A {int a;};
struct B : public A {char b;};

int main()
{
  B b;
  typedef std::pair<A*, A*> MyPair;
  std::vector<MyPair> v;
  v.push_back(std::make_pair(&b, &b)); //compiler error should be here(pair<B*,B*>)
  return 0;
}

I don't understand why this compiles (, maybe somebody may kindly provide detailed explanation? Is it something related to name look-up?

Btw, on Solair,SunStudio12 it doesn't compile : error : formal argument x of type const std::pair<A*, A*> & in call to std::vector<std::pair<A*,A*> >::push_back(const std::pair<A*, A*> & ) is being passed std::pair<B*, B*>

+12  A: 

std::pair has a constructor template:

template<class U, class V> pair(const pair<U, V> &p);

"Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed." (C++03, 20.2.2/4)

Conversion from a derived class pointer to a base class pointer is implicit.

James McNellis
+1 for short and precise answer :-)
Prasoon Saurav
I'm not convinced, make_pair is a template function, which deducts types from arguments. Right?
yurec
Yes, and it makes a `std::pair(B*,B*)`. It then uses the above constructor to construct an `std::pair(A*,A*)` out of it.
GMan
Reread the reply. It's indeed very clear. Thx.
yurec
A: 

Because B is derived from A, the vector v will contain pointers to base class structures of the object b. therefore, you could access the members of A, i.e.

std::cout << v[0].first->a;

EDIT: My mistake, as pointed out below, you can still cast to pointers of type B since the vector is of pointers, not objects, so no object slicing has occurred.

A call such as

std::cout << v[0].first->b; 

will not compile since the elements in the vector are base class pointers and cannot point to derived class members without a cast, i.e.

 std::cout << static_cast<B*>(v[0].first)->b; 

Also note that a dynamic cast, as in

std::cout << dynamic_cast<B*>(v[0].first)->b;  

will not compile with the following error in gcc:

cast.cpp:14: error: cannot dynamic_cast ‘v.std::vector<_Tp, _Alloc>::operator[] [with _Tp = std::pair<A*, A*>, _Alloc = std::allocator<std::pair<A*, A*> >](0u)->std::pair<A*, A*>::first’ (of type struct A*’) to type struct B*’ (source type is not polymorphic)
tmatth
The members of class B have not been lost, and the dynamic type of the objects pointed to is still B. Members of B can still be accessed: `static_cast<B*>(v[0].first)->b` (though in this case, a `dynamic_cast` would probably be preferable).
James McNellis
@tmatth: As James McNellis says, you can still access the derived class members via a cast. Maybe you're thinking of object slicing? That occurs only when using a vector of objects (`A`), not a vector of pointers (`A*`). In that case, yes, members of `B` will be silently discarded.
j_random_hacker
i edited to reflect your feedback, hopefully this clears it up.
tmatth
The `dynamic_cast` fails because `A` is not a polymorphic type; you have to add a virtual function to `A`.
James McNellis