Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

copy and swap idiom with pure virtual class

I am trying to implement virtual class with pure virtual method and 'copy and swap' idiom, but I've encountered some problems. Code won't compile because I am creating instance in the assign operator of the class A which contains pure virtual method.

Is there a way how to use pure virtual method and copy and swap idiom?

class A
{
public:
    A( string name) :
            m_name(name) { m_type = ""; }
    A( const A & rec) :
            m_name(rec.m_name), m_type(rec.m_type) {}
    friend void swap(A & lhs, A & rhs)
    {
        std::swap(lhs.m_name, rhs.m_name);
        std::swap(lhs.m_type, rhs.m_type);
    }

    A & operator=( const A & rhs)
    {
        A tmp(rhs); 
        swap(*this, tmp);
        return *this;
    }

    friend ostream & operator<<( ostream & os,A & x)
    {
         x.print(os);
         return os;
    }

protected:
    virtual void print(ostream & os) =0;    

    string m_type;
    string m_name;
};

class B : A
{
public:
    B(string name, int att) :
        A(name),
        m_att(att)
        {
            m_type="B";
        }

    B( const B & rec) :
        A(rec),
        m_att(rec.m_att) {}

    friend void swap(B & lhs, B & rhs)
    {
        std::swap(lhs.m_att, rhs.m_att);
    }

    B & operator=( const B & rec)
    {
        B tmp(rec) ;
        swap(*this, tmp);
        return *this;
    }

private:
    virtual void print(ostream & os);

    int m_att;

};

Error message:

In member function ‘A& A::operator=(const A&)’:|
error: cannot declare variable ‘tmp’ to be of abstract type ‘A’|
because the following virtual functions are pure within ‘A’:|
virtual void A::print(std::ostream&)|
like image 428
Rob Avatar asked May 06 '14 14:05

Rob


People also ask

What is the copy-and-swap idiom?

What is the copy-and-swap idiom? The simple answer that can explain this idiom is by saying is a way of implementing the assignment operator in terms of a swap function. And it all started with the copying of pointer members of a class.

Is copy-and-swap the same in C++ as in C?

In C++, there is a similar concept, an idiom, called copy-and-swap. But are the motivations the same? Not exactly. Even though I can imagine a situation where there is a global variable that can be used by different threads and it’s crucial limiting the time spent updating that variable. There is something more important.

How to swap two objects of the same class in Java?

The swap function has to be able to swap two objects of the same class, do it, member, by member, and without throwing any exception. The swap function should swap, or in other words, exchange the content of two objects, member by member.


2 Answers

As your compiler informs you, you cannot create a variable of abstract type. There is no way of dancing around that.

This leaves you three main options:

Stop using pure virtual functions

First, you could just get rid of the pure virtual methods and provide a little stub in each of them that calls std::terminate, which would obviously break compile time detection of whether all (former) pure virtual methods are overridden in all derived classes.

This will cause slicing, since it will only copy the base class and everything that makes out the derived class is lost.

Use a stub class w/o pure virtual functions

Similar to that, you could create a derived class that implements all virtual methods with simple stubs (possibly calling std::terminate), and is used only used as a "instantiatable version of the base class".

The most important part to implement for this class would be a constructor that takes a const reference to the base class, so you can just use it instead of copying the base class. This example also adds a move constructor, because I am a performance fetishist.

This causes the same slicing problem as the first option. This may be your intended result, based on what you are doing.

struct InstantiatableA : public A {
    InstantiatableA(A const& rhs) : A(rhs) { }
    InstantiatableA(A&& rhs) : A(::std::move(rhs)) { }

    void print(ostream&) override { ::std::terminate(); }
};

A& A::operator=(InstantiatableA rhs) {
    using ::std::swap;
    swap(*this, rhs);
    return *this;
}

Note: This is really a variable of type A, although I said it could not be done. The only thing you have to be aware is that the variable of type A lives inside a variable of type InstantiatableA!

Use a copy factory

Finally, you can add a virtual A* copy() = 0; to the base class. Your derived class B will then have to implement it as A* copy() override { return new B(*this); }. The reason dynamic memory is necessary is because your derived types may require arbitrarily more memory than your base class.

like image 96
gha.st Avatar answered Sep 29 '22 10:09

gha.st


You're just facing the fact that inheritance works awkwardly with copy semantics.

For instance, imagine you found a trick to pass the compiling phase, what would mean (following example uses assignment but the issue is the same with a copy) :

// class A
// a class B : public A
// another class C : public A inheriting publicly from A
// another class D : public B inheriting publicly from B
B b1;
C c1;
D d1;

// Which semantic for following valid construction when copy/assignment is defined in A ?
b1 = c1;
b1 = d1;

A &ra = b1;
B b2;

// Which semantic for following valid construction when copy/assignment is defined in A ?
ra = b2;
ra = c1;
ra = d1;
like image 43
Qassim Avatar answered Sep 29 '22 09:09

Qassim