0

So I have a vector of pointers holding a baseclass. I create two elements inside the vector and then attempt to swap them after a few layers of abstraction. Currently this causes the compiler to explode and throw several errors inside move.h that look like this:

*c:\program files (x86)\codeblocks\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/move.h: In function 'void std::swap(_Tp&, _Tp&) [with _Tp = Base]':
D:\My Documents\pointertest2\main.cpp:52:   instantiated from here
c:\program files (x86)\codeblocks\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/move.h:81: error: cannot allocate an object of abstract type 'Base'
D:\My Documents\pointertest2\main.cpp:7: note:   because the following virtual functions are pure within 'Base':
D:\My Documents\pointertest2\main.cpp:11: note:     virtual int Base::GetInt()
c:\program files (x86)\codeblocks\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/move.h:81: error: cannot declare variable '__tmp' to be of abstract type 'Base'
D:\My Documents\pointertest2\main.cpp:7: note:   since type 'Base' has pure virtual functions*

The code that causes this problem is as follows:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

class Base {
  public:

    virtual int GetInt() = 0;
    int a;
};

class Test : public Base {
  public:

    int GetInt()
    {
        return a;
    }
};

class Slot {
  public:

    Base *connected;
};

int main()
{
    std::vector<Base*> testVec;

    Base *test = new Test;
    testVec.push_back(test);
    testVec[0]->a = 1;

    Base *test2 = new Test;
    testVec.push_back(test2);
    testVec[1]->a = 2;

    Slot slot;
    slot.connected = testVec[0];

    Slot slot2;
    slot2.connected = testVec[1];

    Slot* slottemp = &slot;
    Slot* slottemp2 = &slot2;

    std::swap(*slottemp->connected, *slottemp2->connected);

    cout << testVec[0]->GetInt() << endl;
    cout << testVec[1]->GetInt() << endl;

    return 0;
}

You can see at the end i'm hoping that testVec[0] will return 2, and testVec[1] will return 1, as these are the swapped values i'm looking for.

Any help would be greatly appreciated, my head has been exploding on this one. I'm entirely open to alternate methods of swapping the pointers contained as elements 0 and 1, this is just where i've ended up at so far.

dr12
  • 3
  • 1
  • 3

2 Answers2

2

You're trying to swap objects of type Base.

slottemp->connected is of type Base *, so by swapping *slottemp->connected, you're swapping two Base &. Since there's no std::swap overload for Base &, the compiler falls back to the default, which goes something like this:

template <class T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;
}

If we instantiate this on T = Base, then we try to construct a temporary Base for the swap; this fails, because Base has pure virtual functions.

One option is to write a std::swap overload for Base:

namespace std {
void swap(Base &a, Base &b) {
  std::swap(a.a, b.a);
}
}

This will allow you to swap the underlying Bases. However, this is unlikely to be well-suited to a virtual base class - what happens if someone tries to swap a Foo and a Bar, both derived from Base?

If you just want the Slots swapped, you can swap the Base *s, not the Bases themselves:

std::swap(slottemp->connected, slottemp2->connected);

If you want the changes to be reflected in the vector, you should make Slot hold an iterator, or a pointer to the pointer inside the vector

class Slot {
  public:
    Base **connected;
};

// ...
slot.connected = &testVec[0];
// ...

std::swap(*slot.connected, ...);

In this particular case, you know the true type is Test, so you can also cast to this concrete type and swap that way:

std::swap(*(Test *)slottemp->connected, *(Test *)slottemp2->connected);

I wouldn't recommend this approach, as it invites the possibility of slicing (or worse, illegal casting) later if you start adding values of type other than Test to the vector.

Community
  • 1
  • 1
bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • Thanks for explaining that, the problem makes a lot more sense now. Edit: just seen your updated answer - that seems to be what i'm looking for, thanks. – dr12 Jul 17 '11 at 19:24
  • @dr12, it's all about what you're swapping. See my third example if you want the vector to be updated. – bdonlan Jul 17 '11 at 19:26
  • Many thanks, the third example is exactly what i'm looking for – dr12 Jul 17 '11 at 19:27
0

I'd say you are trying to swap objects and not pointers(the * is getting the object where connected points, not connected as a ptr), and swap can't do it because only concrete objects can be swapped, they have to have a size, abstract classes don't have a size.
Try:

std::swap(slottemp->connected, slottemp2->connected);

so that you only swap pointers.

You have

slottempptr(addr01)  -> vec[0]
slottempptr2(addr02) -> vec[1]

And you do:

swap(slottempptr,slottempptr2)

So you get:

slottempptr2(addr02)  -> vec[1]
slottempptr(addr01)   -> vec[0]

But vec[0] and vec[1] are unchanged, you just changed slottempptr

Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
  • Unfortunately doing that results in an output of 1,2 - so it seems to act as if nothing has been swapped! – dr12 Jul 17 '11 at 19:17
  • Because you are not swapping the pointers inside the vectors, you are merely swapping the external pointers from slottemps to v[0] and v[1], you are not exchanging the ones in the vector but you always print from the vector. – Arkaitz Jimenez Jul 17 '11 at 19:23
  • Ah, thanks. What i'm actually attempting to achieve is the swapping of the pointers in testVec so that testVec[1] becomes testVec[0] and vice versa – dr12 Jul 17 '11 at 19:25
  • For that you have to do the swap like `swap(vec[0],vec[1])` You want to exchange ptrs inside the vector, for that you have to address the positions in the vector directly. You can try with pointers to pointers and get `ptr1 = &vec[0]` and `ptr2 = &vec[1]` and do `swap(*ptr1,*ptr2)` – Arkaitz Jimenez Jul 17 '11 at 19:27