0

So I go this:

class A;

class B : public A;

class C : public B;

vector<A*> *vecA;

vector<C*> *vecC;

And I want to cast a vectC into a vecA.

vector<A*> *_newA = static_cast< vector<A*>* >(vecC); //gives an error

So I used void pointer as a buffer and the cast:

void *buffer = vecC;

vector<A*> *_newA = static_cast< vector<A*>* >(buffer); //works

Is this valid? Is there another way to do it?

nahpr
  • 619
  • 7
  • 15

5 Answers5

4

You should just have a std::vector<A*> and then when you wish to put a B or C inside, you can do this:

 std::vector<A*> vec;
 vec.push_back(b);

This will work much better than anything you're currently doing.

Also, you should use std::unique_ptr or std::shared_ptr, depending on ownership semantics of your pointers. Owning raw pointers are a no-no!!

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
3

Is this valid?

No it's not. It is completely undefined what happens if you access it.

Is there another way to do it?

Yes. It depends on what you want to do:

  1. copy the content: std::copy(vecC.begin(), vecC.end(), std::back_inserter(vecA));

  2. create an adaptor which behaves as random access container of A* if given a container or iterator of C* or any other derived type

  3. I am sure there are other solutions, just tell us what you want to do

Paul Michalik
  • 4,331
  • 16
  • 18
  • the "completely undefined" is only in the formal. in practice, there is no special specialization of `vector` that could wreak havoc, nor is there any difference in pointer value offsets, nor is the base class non-polymorphic and a virtual introduced in derived class (which could make the base and derived pointers to the same object have different bitpatterns). in short, one must be very careful. but in practice, with such care taken, the conversion works. that said, it allows ungood things. see my answer for that. – Cheers and hth. - Alf Aug 06 '12 at 16:11
  • @Alf: In practice, it's a violation of strict aliasing and may cause all sorts of brokenness after optimization. – Ben Voigt Aug 06 '12 at 16:47
  • @Alf: 'undefined' means the standard does not tell what happens if the points you enumerate were not valid - which it does not guarantee... As you correctly claim, it could even work, but, strictly speaking, it also falls under 'undefined' :-) Anyway it hurts a superposed principle which could cause any other sort of bad things... – Paul Michalik Aug 06 '12 at 16:50
1

It looks to me as if you're looking for covariant support for generic (template) types. This is not supported at all by C++. It is supported by the CLR - and C#/VB - though only in the latest versions.

In spite of this, to echo others' responses, you're likely barking up the wrong tree. My hunch is that you want to have a vector of pointers to A-typed objects... and this type should include virtual methods and a virtual destructor - as necessary. It's impossible to be more specific about a suitable alternative approach without a better understanding of the high-level problem you're trying to solve - which is not evident from your question.

aSteve
  • 1,956
  • 1
  • 20
  • 35
1

The magic words you probably needed to know are "covariance" and "contravariance". Have a look here for a very closely related issue.

Short answer: there's no neat, sensible way to do the conversion you want to do merely with casts. You will need to copy to contents of your old vector the long way into your new vector.

But more importantly: 1. Don't ever use new on a standard container type. 2. Don't pass around pointers to standard container types. 3. Don't pass around raw pointers to your own objects, use std::shared_ptr instead.

Community
  • 1
  • 1
Rook
  • 5,734
  • 3
  • 34
  • 43
  • All three of your parting recommendations are over-simplified and incorrect as written. – Ben Voigt Aug 06 '12 at 16:48
  • @BenVoigt: that's a startlingly unhelpful piece of feedback. Well done. – Rook Aug 06 '12 at 17:15
  • @nahpr: Yep, passing by reference is quite sensible. – Rook Aug 06 '12 at 17:17
  • 1
    @Rook: Sorry, I looked just at your screen name and not your reputation, thought you were a more experienced user. Anyway: (1) Using `new` on a standard container type and then sticking the pointer inside an RAII smart pointer is perfectly reasonable. (2) If passing by reference is reasonable, so is passing by pointer. We're not talking about lifetime extension of temporaries (which is only available for references). (3) Raw pointers are the iterator type for some containers, and it makes good sense to use them as iterators (i.e. non-owning and shorter lifetime than the container) – Ben Voigt Aug 06 '12 at 18:42
  • And that's only a few examples where your guidance doesn't hold, there are more. – Ben Voigt Aug 06 '12 at 18:43
  • 1
    Here's another good exposition of the principle that smart pointers are not best in all circumstances: http://stackoverflow.com/a/7658089/103167 – Ben Voigt Aug 06 '12 at 18:54
  • @BenVoigt: thanks. I was mostly keen on you pointing out where I was incorrect for the OP's benefit! Anyway, can you give a useful example of when you might want to stick a standard container in a smart pointer? I guess wrapping one in a `unique_ptr` might be useful. Whilst (2) is a stylistic choice, I can't see a good reason to use pointers to containers over references in general use; for example, the standard containers public API does not use pointers, IIRC. (3) is not quite so relevant to the OP's code, but you are correct that non-owning references/pointers are often sensible. – Rook Aug 06 '12 at 19:00
  • @Rook: Perhaps you write a function to trace the route to a particular host on the Internet. You might return the distance in hops, and have a parameter of type `std::vector*`. If a vector is provided, it is filled in with that information. But the client can get just the hop count by passing NULL, in which case the function saves time by not looking up the host names. – Ben Voigt Aug 06 '12 at 19:10
  • This was mentioned fairly recently in another C++ question. I favoured the function overloading option, myself. – Rook Aug 06 '12 at 19:13
  • @Rook: Then you're stuck with code duplication. An optional argument with a default value of `0` is a clean way to provide both signatures without duplication. And yes, you could hide that behind a facade of overloaded public functions, but that's adding extra code for no purpose. – Ben Voigt Aug 07 '12 at 03:49
1

Instead of detouring via void*, with C++11 just use reinterpret_cast.

However, the conversion that you desire permits very ungood things:

#include <iostream>
#include <vector>
using namespace std;

class Base {};

class Derived: public Base
{ public: int x; Derived(): x(42) {} };

int main()
{
    vector< Derived* >  v;
    Derived             d;

    v.push_back( &d );
    cout << v.back()->x << endl;        // 42

    vector< Base* >*    pbv =
        reinterpret_cast< vector< Base* >* >( &v );
    Base                b;

    pbv->push_back( &b );
    cout << v.back()->x << endl;        // indeterminate value
}

It's roughly the same issue that you have with converting T** to T const** (not permitted, could allow ungood things). The short of it is, don't do this unless you know exactly what you're doing. The longer of it, hm, well, there's not room to discuss it here, but it involves differentiating between mutable and immutable collections, and being very very careful.


edit: as others (who did not address the conversion question) have already stated, a practical solution is a vector of A pointers, or smart pointers, and then using C++ polymorphism (i.e. virtual member functions).
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • -1 for implying that "ungood things" can only happen when writing to the container. – Ben Voigt Aug 06 '12 at 18:51
  • @Ben: maybe you're thinking of strict aliasing rules. that's a broken compiler option for a particular compiler (namely for gcc), not a broken language. note that the list of supported conversions that is sometimes cited in support of g++'s behavior, never included conversion from first element of struct to struct itself and vice versa, which is explicitly supported elsewhere in the standard. i.e. that list has never been complete, which it would have to be in order to be the support that sometimes is claimed. – Cheers and hth. - Alf Aug 06 '12 at 21:51
  • @Alf: Any dereference of the pointer created via `reinterpret_cast` is undefined behavior. Strict aliasing happens to be one way in which your code may break even if the memory layout happens to match up. And no, strict aliasing is part of the language. It isn't a "broken language", but it does make your suggested code broken. Because there's a lot of broken code out there, gcc provides as option to disable optimizations associated with aliasing analysis, but the strict aliasing rule is not gcc-specific. – Ben Voigt Aug 07 '12 at 03:47
  • @Ben: you're right that `reinterpret_cast` is formally undefined behavior. but it's in the language to be used: the system software on all three major desktop platforms relies on compiler defining this behavior. and no, strict aliasing is not part of the language, and is not mentioned in the standard. read again what i wrote (the list is incomplete). i did not add more powerful arguments because there's no space to discuss it, and formal incompleteness of the list is enough by itself, but rest assured it's wholly a gcc option issue, just as the floating point optimization in that compiler. – Cheers and hth. - Alf Aug 07 '12 at 07:16