Pregunta

I've a vector of pair which I need to copy them linearly to a vector of ints. I've the following code which works well, but I'm not sure if it's safe considering struct padding issues in C++.

std::vector < std::pair<int, int> > test_vector;
for (int i=0;i<5;i++) {
    test_vector.push_back(std::make_pair(i,i*5));
}
std::vector<int> int_vec(test_vector.size() * 2);
std::copy(reinterpret_cast<int*>(&(*test_vector.begin())),reinterpret_cast<int*>(&(*test_vector.end())),int_vec.begin());

Now, my question is - Is the above code safe? If not, is there an elegant way to do it without writing a loop?

¿Fue útil?

Solución

How about std::transform and a lambda function ?

std::vector<int> v;
std::transform(test_vector.begin(), test_vector.end(), std::back_inserter(v), 
               [&v](const std::pair<int, int> &p) 
               { v.push_back( p.first);
                 return p.second ;});

If you can't use C++11, and probably "hate" doing linear copying using loops

You can use functor like:

struct X{
    X(std::vector<int> &x) :v(x){}
    int operator () (const std::pair<int, int> &p)
    {
        v.push_back(p.first);
        return p.second;
    }
    std::vector<int> &v;
};

std::vector<int> v; //Final vector

std::transform(test_vector.begin(), 
               test_vector.end(), 
               std::back_inserter(v), 
               X(v));

std::vector<int> ::iterator it;

for(it=v.begin() ; it!=v.end() ;++it)
  std::cout<<*it<<" ";

Otros consejos

You don't need any fanciness for this problem. A simple for loop will do, especially if you can't use C++11

std::vector < std::pair<int, int> > test_vector;
std::vector<int> int_vec; int_vec.reserve(test_vector.size() * 2);
for (std::vector < std::pair<int, int> >::const_iterator it = test_vector.begin(), end_it = test_vector.end(); it != end_it; ++it)
{
    int_vec.push_back(it->first);
    int_vec.push_back(it->second);
}

You're right to be concerned about structure padding issues, but I think you haven't really faced the central assumption that your code is making:

Can I treat a std::pair<int, int> as an array of two integers, with the .first being the first element in the array and .second being the second element?

From a "correctness" point of view, I'd say "no". You've identified padding issues, but there's also the ordering of the fields. There's really no guarantee that .first has a lower memory address than .second.

From a "practical" point of view, I'd be quite surprised your that code did not work. [ Edit: Neil has pointed out a concrete example where there are padding issues; so color me surprised. Besides being "bad form", I now consider the code broken in practice. ]

As for a solution, you can use for_each with a custom action that pushes both elements of the pair (untested code)

struct action {
    action ( vector<int> & target ) : t_(target) {}
    void operator () ( const pair &p ) const 
        { t_.push_back(p.first); t_.push_back(p.second); }
private:
    vector<int> &t_;
}

for_each ( test_vector.begin(), test_vector.end(), action(v));

A reinterpret_cast is usually bad news. Wouldn't you be better off reserve()ing enough space in the destination vector and then calling std::for_each on the source vector of pairs and then have the function/lambda push_back both first and second into the destination vector ?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top