Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::reverse on boost::ptr_vector slices objects?

Let Base and Derived be classes with data members:

class Base {
public:
    Base(int i):f(i)     { }
    virtual void print() { cout << "base " << f << endl; }
    int f;
};

class Derived: public Base {
public:
    Derived(int i):Base(0),g(i) {  }
    void print() { cout << "derived " << g << endl; }
    int g;
};

Now create some instances of Base and Derived on the heap and store them in a boost::ptr_vector:

int main(int argc, char *argv[])
{
    boost::ptr_vector<Base> v;
    v.push_back(new Derived(1));
    v.push_back(new Base(2));
    v.push_back(new Base(3));
    v.push_back(new Derived(4));

print all the objects:

    for (std::size_t i(0); i != v.size(); ++i)
        v[i].print();

then reverse and print again:

    std::reverse(v.begin(), v.end());
    for (std::size_t i(0); i != v.size(); ++i)
        v[i].print();
}

This program prints:

derived 1
base 2
base 3
derived 4
derived 1
base 3
base 2
derived 4

std::reverse() on boost::ptr_vector calls std::iter_swap() which in turn calls std::swap which swaps items by creating a temporary copy.

However, the temporary copy does slice the Derived objects. As you can see, the int g of the Derived objects 1 and 4 are not swapped, so the objects are broken after the swap.

This behavior puzzles me. Isn't boost::ptr_vector a container to avoid exactly this kind of problem?

What I'd need here is a virtual copy constructor, which doesn't exist in C++.

How can I work around this? Do I need to implement a virtual swap function for Base, so that virtual dispatching calls another swap member function of Derived?

EDIT: Why copy objects in the first place? As the vector only stores pointers, reversing it should be a swap of the pointers, not the objects pointed to! Is there a function that does that?

like image 244
Philipp Avatar asked Jul 19 '14 15:07

Philipp


1 Answers

Boost's ptr_vector class contains member functions whose job is manipulate the underlying pointers. But the public interface largely hides the fact that the vector internally stores pointers. The idea is to produce something that behaves as if it contained the objects directly but internally stores pointers to avoid slicing when you put an object into the container. But if you copy an object out of the container into an object of the base type, then it will slice. So you must not call such functions on ptr_vectors.

This means, unfortunately, that before you apply any algorithm to a ptr_vector, you need to understand exactly what it does.

like image 100
David Schwartz Avatar answered Sep 28 '22 06:09

David Schwartz