14

This seems like a pretty basic problem, but I can't figure it out. I have a std::vector of raw pointers to Derived objects, and I just want to copy it to another vector of Base pointers using the assignment operator. With VC++ I get error C2679 "binary '=': no operator found..." BTW I don't want a deep copy of the objects, I just want to copy the pointers. Sample code:

#include <vector>
using namespace std;

struct Base{};    
struct Derived: public Base {};

int main (int argc, char* argv[])
{
    vector<Derived*> V1;
    vector<Base*> V2;
    V2 = V1;  //Compiler error here
    return 0;
}

What confuses me is that I can copy the vector by looping through it and using push_back, like this:

for (Derived* p_derived : V1)
    V2.push_back(p_derived);

So my question is why does the assignment fail, while push_back works? Seems like the same thing to me.

Carlton
  • 4,217
  • 2
  • 24
  • 40

3 Answers3

24

That's because while Base and Derived have a relationship, there is no relationship between vector<Base*> and vector<Derived*>. As far as class hierarchy is concerned, they are entirely unrelated, so you can't assign one to the other.

The concept you are looking for is called covariance. In Java for instance, String[] is a subtype of Object[]. But in C++, these two types are just different types and are no more related than String[] and Bar.

push_back works because that method just takes a T const& (or T&&), so anything convertible to a Base* will be acceptable - which a Derived* is.

That said, vector has a constructor that takes a pair of iterators, which should be easier to use here:

vector<Base*> v2(v1.begin(), v1.end());

Or, since it is already constructed:

v2.assign(v1.begin(), v1.end());
Barry
  • 286,269
  • 29
  • 621
  • 977
  • I suppose the assignment operator would become overly complicated if it had to accept vectors of different-but-related types. – Carlton Apr 07 '15 at 15:15
  • 1
    This is a (pretty common) C++ FAQ as well. See [this](https://isocpp.org/wiki/faq/containers#container-ptr-conversion). – edmz Apr 07 '15 at 20:47
  • In C# `List` is also unrelated to `List` but `IEnumerable` is a subtype of `IEnumerable` because `IEnumerable` is variant. – usr Apr 07 '15 at 21:15
  • @usr *co*variant. To go along with the FAQ example, that doesn't violate type safety because `IEnumerable` is a read-only interface, so you can't break the `vector`. However, one could imagine in a system such that that `vector` is still invariant but there existed a `const_vector`, which could be covariant. – Barry Apr 07 '15 at 21:34
9

push_back performs element-wise conversions. The assignment operator exists only between vectors of the same type.

An easy solution is to use assign:

v2.assign(v1.begin(), v1.end());
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

In the general case of templates, if you have a class template

template <typename T> struct Foo {};

Foo<Base> is not a base class of Foo<Derived>.

Hence, you cannot do:

Foo<Derived> f1;
Foo<Base> f2 = f1;
R Sahu
  • 204,454
  • 14
  • 159
  • 270