C++: converting a container to a container of different yet compatible type

后端 未结 8 898
南笙
南笙 2021-01-05 08:28

It often happens to me to have a a container C (or whatever kind of wrapper class, even smart pointers) for a type T1, and want to convert such

相关标签:
8条回答
  • 2021-01-05 09:10

    It's absolutely not guaranteed that those containers are binary compatible and could be casted with something like reinterpret_cast<>.

    For example, if the container (like std::vector) stores the data internally in a C-style array, C<T1> would contain a T1[] array while C<T2> would contain a T2[]. If now T1 and T2 have different sizes (for example T2 has more member variables) the memory of the T1[] can not simply be interpreted as a T2[] since the elements of these arrays would be located at different positions.

    So simply interpreting the C<T1> memory as a C<T2> won't work and a real conversion is necessary.

    (Furthermore there might be template specializations for different types, so that C<T1> might look completely different than C<T2>)

    For converting one container to another see for example this question or many other related ones.

    0 讨论(0)
  • 2021-01-05 09:13

    Besides all the other issues dealt by others:

    • conversion does not imply same memory footprint (think conversion operations...)
    • potential specializations of the template class (container in your question, but from the point of view of the compiler a container is just another template) even if the types are themselves binary compatible
    • unrelated-ness of different instantiations of the same template (for the general case)

    There is a basic problem in the approach that is not technical at all. Provided that an apple is a fruit, neither a container of fruits is a container of apples (trivially demonstrated) nor a container of apples is a container of fruit. Try to fit a watermelon in a box of apples!

    Going to more technical details, and dealing specifically with inheritance where no conversion is even required, (a derived object is already an object of the base class), if you were allowed to cast a container of the derived type to the base type, then you could add invalid elements to the container:

    class fruit {};
    class apple : public fruit {};
    class watermelon : public fruit {};
    std::vector<apple*> apples = buy_box_of_apples();
    std::vector<fruit*> & fruits = reinterpret_cast< std::vector<fruit*>& >(apples);
    fruits.push_back( new watermelon() ); // ouch!!!
    

    The last line is perfectly correct: you can add a watermelon to a vector<fruit*>. But the net effect is that you have added a watermelon to a vector<apple*>, and in doing so you have broken the type system.

    Not everything that looks simple in a first look is in fact sane. This is similar to the reason why you cannot convert an int ** to a const int ** even if the first thought is that it should be allowed. The fact is that allowing so would break the language (in this case const correctness):

    const int a = 5;
    int *p = 0;
    int **p1 = &p;       // perfectly fine
    const int **p2 = p1; // should this be allowed??
    *p2 = &a;            // correct, p2 points to a pointer to a const int
    **p1 = 100;          // a == 100!!!
    

    Which brings us back to the example you provided in one of the comments to another answer (to prove the point in general, I'll use a vector and instead of a set since set contents are immutable):

    std::vector<int*> v1;
    std::vector<const int*> &v2 = v1; // should this be allowed?
    const int a = 5;
    v2.push_back( &a );  // fine, v2 is a vector of pointers to constant int
                         // rather not: it IS a vector of pointers to non-const ints!
    *v1[0] = 10;         // ouch!!! a==10
    
    0 讨论(0)
提交回复
热议问题