Search code examples
c++vectorstdallocatorreinterpret-cast

How reinterpret_cast works for flattening a std::vector?


I would like to represent a std::vector of a structure containing several integers as a "flatten" vector of integers, without copying the data.

I tried something with a reinterpret_cast as shown below:

#include <vector>
#include <iostream>

struct Tuple
{
    int a, b, c;
};

int main()
{
    // init
    std::vector<Tuple> vec1(5);
    for(size_t i=0; i<vec1.size(); ++i)
    {
        vec1[i].a = 3 * i + 0;
        vec1[i].b = 3 * i + 1;
        vec1[i].c = 3 * i + 2;
    }   

    // flattening
    std::vector<int>* vec2 = reinterpret_cast<std::vector<int>*>(&vec1);

    // print
    std::cout << "vec1 (" << vec1.size() << ")  : ";
    for(size_t i=0; i<vec1.size(); ++i)
    {
        std::cout << vec1.at(i).a << " " << vec1.at(i).b << " " << vec1.at(i).c << " ";
    }
    std::cout << std::endl;

    std::cout << "vec2 (" << vec2->size() << ") : ";
    for (size_t j = 0; j < vec2->size(); ++j)
    {
        std::cout << vec2->at(j) << " ";
    }
    std::cout << std::endl;

    return 0;
}

which works well since the output is:

vec1 (5)  : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 
vec2 (15) : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

My questions are :

  • Is this behavior compiler dependent? (I am using g++ 6.3.0)
  • How vec2 knows that the size of the vector is 15 and not 5?
  • Is their any other solution avoiding the use of reinterpret_cast? (If I "accidentally" add a double member to Tuple, the resulting issue could be hard to track...)
  • If vec1 has a specific allocator: std::vector<Tuple,A<Tuple>>, what should be the type of vec2? std::vector<int> or std::vector<int,A<int>> or std::vector<int,A<Tuple>>?

Solution

  • You can't legally reinterpret_cast the entire vector to a different type of vector. But you can legally cast a pointer to struct to a pointer to the first element of that struct. So this works:

    std::vector<Tuple> vec1(5);
    int* vec2 = &vec1.front().a;
    size_t vec2_size = vec1.size() * sizeof(vec1[0]) / sizeof(vec2[0]);
    
    for (size_t j = 0; j < vec2_size; ++j)
    {
        std::cout << vec2[j] << " ";
    }
    

    You need to make sure there's no padding in Tuple, so:

    static_assert(sizeof(Tuple) == 3 * sizeof(int), "Tuple must be 3 ints");
    

    To answer your bulleted questions:

    • Is this behavior compiler dependent?
      • Your code was illegal.
    • How vec2 knows that the size of the vector is 15 and not 5?
      • You got lucky, your code was illegal.
    • Is their any other solution avoiding the use of reinterpret_cast?
      • See above.
    • If vec1 has a specific allocator: std::vector>, what should be the type of vec2?
      • Same as above, int*.