CRTP derived class loses its members and segfaults after using inherited assignment operator?

I'm trying to use the 4 1/2 rule of move semantics and remove duplication from a process using CRTP. This turned out to be difficult because, despite the compilation, the following code ends up with a segfault when I try to access the members of the derived class after using the assignment operator. Why is this happening and is there a way to get around it?

CRTP base class

// BaseCRTP.h

template<class Derived>
class BaseCRTP {
public:
    BaseCRTP() {};

    BaseCRTP(const BaseCRTP &rhs) {
        static_cast<Derived *>(this)->setCore(static_cast<const Derived&>(rhs));
    };

    BaseCRTP(BaseCRTP &&rhs) {
        static_cast<Derived *>(this)->swap(rhs);
    }

    Derived &operator=(BaseCRTP rhs){
        static_cast<Derived *>(this)->swap(rhs);
        Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
        return *static_cast<Derived*>(this); // something happens here that causes issue? 
    }
};

      

Derived class

// Derived.h

#include "Base.h"
#include <algorithm>

class Derived : public BaseCRTP<Derived>{
private:
    int m_member1;
public:
    using BaseCRTP<Derived>::BaseCRTP;
    using BaseCRTP<Derived>::operator=;

    Derived(int member);
    void setCore(const Derived& rhs);
    void swap(BaseCRTP<Derived> & rhs);
    int getMember() const;
};

      

// Derived.cpp

#include "Derived.h"

void Derived::setCore(const Derived &rhs) {
    m_member1 = rhs.m_member1;
}

void Derived::swap(BaseCRTP<Derived> &rhs) {
    Derived& rhs_p = static_cast<Derived&>(rhs);
    std::swap(m_member1, rhs_p.m_member1); // members have correct values in debugger
}

Derived::Derived(int member) {
    m_member1 = member;
}

int Derived::getMember() const{
    return m_member1;
}

      

home

// main.cpp

#include <iostream>
#include "Derived.h"

int main() {
    Derived d(1);
    int z = d.getMember(); // works fine
    Derived dd(34);
    int w = dd.getMember(); // works fine
    d = dd; // after this d and dd no longer have m_member1 values
    int y =  dd.getMember();  //segmentation fault
    int x =  d.getMember();   // when swapped this also segmentation faults
    std::cout << z << w << y << x << std::endl;
    return 0;
}

      

UPDATE:

I originally changed void swap(BaseCRTP<Derived> & rhs);

to use the parent class as it didn't compile with that and the debugger seemed to indicate that members were supported. I tried to switch it back with no luck, now the function reads:

void Derived::swap(Derived &rhs) {
    std::swap(m_member1, rhs_p.m_member1);
}

      

a Derived &operator=(BaseCRTP rhs)

NOW: Derived &operator=(Derived rhs)

.

This results in the following compile-time errors:

PATH\main.cpp: In function 'int main()':
PATH\main.cpp:9:9: error: ambiguous overload for 'operator=' (operand types are 'Derived' and 'Derived')
     d = dd;
         ^~
In file included from PATH\Derived.h:7:0,
                 from PATH\main.cpp:2:
PATH\Base.h:14:7: note: candidate: constexpr BaseCRTP<Derived>& BaseCRTP<Derived>::operator=(const BaseCRTP<Derived>&) <deleted>
 class BaseCRTP {
       ^~~~~~~~
PATH\Base.h:28:14: note: candidate: Derived& BaseCRTP<Derived>::operator=(Derived) [with Derived = Derived]
     Derived &operator=(Derived rhs){
              ^~~~~~~~
In file included from PATH\main.cpp:2:0:
PATH\Derived.h:10:7: note: candidate: Derived& Derived::operator=(const Derived&) <deleted>
 class Derived : public BaseCRTP<Derived>{
       ^~~~~~~

      

Apparently remote principals are still involved in overload resolution ... this is a bit annoying to say the least. should there really be something like that? it is very clear that the only valid operator is operator=

which I have defined as its only non-remote operator.

UPDATE 2

It all works, of course, if I both change the signature and don't let it be an assingment statement. If instead I use the following function:

Derived& assignmentOperator(Derived rhs){
    static_cast<Derived *>(this)->swap(rhs);
    Derived& d = *static_cast<Derived*>(this); // debugger shows d now has the correct value for m_member, no issue here
    return *static_cast<Derived*>(this); // something happens here that causes issue?
}

      

and aside main.cpp

do:

d.assignmentOperator(dd); 

      

everything works, no compilation errors, see selected answer why my original seg-faulted. I'll post a new question to figure out how I can get around this nasty semantics ...

+3


source to share


1 answer


Derived &operator=(BaseCRTP rhs)

      

This assignment operator splits rhs

into type BaseCRTP<Derived>

when the original type of the argument was Derived

. That the members are "lost".



You can force an operator to take BaseCRTP &&

and instead using

it into Derived

to implement operator=

it:

Derived& Derived::operator= (Derived rhs)
{
  return BaseCRTP::operator= (std::move(rhs));
}

      

+2


source







All Articles