tags:

views:

961

answers:

7

This compiles:

int* p1;
const int* p2;
p2 = p1;

This does not:

vector<int*> v1;
vector<const int*> v2;
v2 = v1;  // Error!
v2 = static_cast<vector<const int*> >(v1);  // Error!

What are the type equivalence rules for nested const pointers? I thought the conversion would be implicit. Besides, I'd rather not implement point-wise assignment of STL containers, unless I really have to.

+7  A: 

The issue is not the pointers, but the types of the two vectors. There are no standard conversions between templated types like those of v1 and v2 in your example.

This is perhaps easier to see in the following code:

#include <vector>
using namespace std;

int main() {
    vector <char> cv;
    vector <int> iv;
    cv = iv; // error
}
anon
+16  A: 

Conversion from int* to const int* is built into the language, but vectors of these have no automatic conversion from one to the other.

Nikolai N Fetissov
+1 for the clarity :)
Johannes Schaub - litb
+31  A: 

Direct assignment is not possible. As others explained, the equivalence is not established by the pointer types, but by the container types. In this case, vector doesn't want to accept another vector that has a different, but compatible element type.

No real problem, since you can use the assign member function:

v2.assign(v1.begin(), v1.end());
Johannes Schaub - litb
+1 for code sample
Nikolai N Fetissov
Why? I would understand being hesitant about implicitly converting vector<char> to vector<int>, but int* to const int*? I thought const receives special treatment in this regard. Any idea why the C++ standard decided against it?
Lajos Nagy
Honestly, i don't know either.
Johannes Schaub - litb
Perhaps something to do with templates are matched exactly, not always the most convenient.
sixlettervariables
@sixlettervariables: Precisely!
David Thornley
I saw this example somewhere: an Apple is a Fruit, but a Bag of Apples is not a Bag of Fruit. It would violate the Liskov Substitutability Principle: you can put an Orange into a Bag of Fruit, but you can't put an Orange into a Bag of Apples. If Bag (or, in your case, vector<T>) were immutable, you wouldn't have this problem.
Marius Gedminas
+3  A: 

It would be perfectly possible to write your own version of vector where this was possible. It would be identical to the standard type, but with a templated version of operator=, something like this:

template <class A>
vector2<T> &operator=(const vector2<A> &other)
{
    assign(other.begin(), other.end());
    return *this;
}

Where T is the element type of the whole class, whereas A is any type assignable to T.

It's not clear to me why std::vector doesn't have this.

Daniel Earwicker
+4  A: 

In C++ templated classes, each instantiation of the template is a completely different class - there is as much difference between vector<int *> and vector<const int *> as there is between vector<int *> and vector<string> or any other two classes for that matter.

It is possible that the committee could have added a conversion operator on vector to vector<U> as Earwicker suggests - and you can go ahead and provide your own implementation of such a function:

template <class A, class T>
vector<T> convert_vector(const vector<A> &other)
{
    vector<T> newVector;
    newVector.assign(other.begin(), other.end());
    return newVector;
}

and use it like so:

vector<int*> v1;
vector<const int*> v2;
v2 = convert_vector<const int*>(v1);

Unfortunately, until C++0x comes with it's move constructors, this will be pretty bad performance-wise.

Eclipse
+1  A: 

Coercion by Member Template idiom is one possible approach to solve the problem. Essentially, a member template copy-assignment operator is added that allows the template class to participate in the same implicit type conversions (coercion) that are otherwise possible only on the type parameters of the class template. Although the idiom is used in the STL in other places, it is not available in std::vector.

Sumant
+1  A: 

Dangerous, unless you know the types are absolutely compatible:

v2 = reinterpret_cast<std::vector<const int *> & >(v1);

Most STL implementations do use a specialization were all vectors of pointers share the same underlying implementation. This is since (void *) is typically the same size as (int *) or any other pointer type.

Juan