Copy-and-Swap idiom for class with references to abstract classes

932 views Asked by At

I'm trying to implement the Copy-and-Swap Idiom for my class, because I need to implement operator=, and since it has reference members, and references can only be assigned once, I thought that the aforementioned idiom was a valid workaround.

But now I'm getting a build error:

>c:\Program Files\Microsoft Visual Studio 10.0\VC\include\utility(102): error C2259: 'IVariables' : cannot instantiate abstract class
1>          due to following members:
1>          'IVariables::~IVariables(void)' : is abstract
1>          d:\svn.dra.workingcopy\serialport\IVariables.h(6) : see declaration of 'IVariables::~IVariables'
1>          'std::string &IVariables::operator [](const std::string &)' : is abstract
1>          d:\svn.dra.workingcopy\serialport\IVariables.h(7) : see declaration of 'IVariables::operator []'
1>          'unsigned int IVariables::getVariableLength(const std::string &) const' : is abstract
1>          d:\svn.dra.workingcopy\serialport\IVariables.h(8) : see declaration of 'IVariables::getVariableLength'
1>          Message.cpp(32) : see reference to function template instantiation 'void std::swap<IVariables>(_Ty &,_Ty &)' being compiled
1>          with
1>          [
1>              _Ty=IVariables
1>          ]

It points me here:

void Swap( CMessage& a, CMessage& b ){
    using std::swap;
    swap( a.m_bIgnoreIncoming, b.m_bIgnoreIncoming );
    swap( a.m_Variables, b.m_Variables );
}

This is the Swap function from the idiom, and m_Variables is indeed a reference to an abstract class. Is it impossible to swap that kind of references? If there is a Boost solution for this, please tell me since I recently started using it.

2

There are 2 answers

5
Mike Seymour On BEST ANSWER

References can't be swapped any more than they can be reassigned. swap will attempt to swap the objects being referred to (by assigning them via a temporary object, if the default std::swap is used). Since they refer to an abstract base class, then this fails since the temporary can't be created.

If you want a reassignable reference, then use a pointer instead.

1
Ignatella On

Here is a short and complete example that illustrates swapping references to abstract type:

#include <iostream>

class AbstractBase
{
public:
     virtual AbstractBase& clone() const = 0;
     virtual AbstractBase& operator=(const AbstractBase& other) = 0;
     virtual ~AbstractBase() = default;
};

class IntWrapper: public AbstractBase
{
public:
    int val;

public:
    IntWrapper(int v) : val{v} { }
    
    IntWrapper& clone() const
    {
        IntWrapper* new_obj = new IntWrapper(val);
        return *new_obj;
    }

    IntWrapper& operator=(const AbstractBase& other) override
    {
        auto context = static_cast<const IntWrapper&>(other);
        val=context.val;
        return *this;
    }

    ~IntWrapper() override = default;
};

void swap(AbstractBase& a, AbstractBase& b)
{
    AbstractBase& tmp = a.clone();
    a = b;
    b = tmp;
    delete &tmp;
}

int main()
{
    IntWrapper a(3), b(4);

    std::cout << a.val << ", " << b.val << std::endl;  
    swap(a,b);
    std::cout << a.val << ", " << b.val << std::endl;

    return 0;
}

And output:

3, 4
4, 3


==1977== 
==1977== HEAP SUMMARY:
==1977==     in use at exit: 0 bytes in 0 blocks
==1977==   total heap usage: 3 allocs, 3 frees, 73,744 bytes allocated
==1977== 
==1977== All heap blocks were freed -- no leaks are possible
==1977== 
==1977== For lists of detected and suppressed errors, rerun with: -s
==1977== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)