I'm trying to use rule of 4 1/2 move semantics and remove duplication from the process using CRTP. This has proved difficult as, despite compiling, the following code ends up segfaulting when I try to access members of the derived class after using the assignment operator. Why does this happen and is there a way around this?
Base CRTP 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;
}
Main
// 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 out doing so, and the debugger seemed to indicate that members were maintained. I've attempted to switch it back with no luck, the function now reads:
void Derived::swap(Derived &rhs) {
std::swap(m_member1, rhs_p.m_member1);
}
with Derived &operator=(BaseCRTP rhs)
now being: 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 deleted members still get to participate in overload resolution ... which is a bit annoying to say the least. surely there must be some way around this? its very clear that the only valid operator is the operator=
I've defined as its the only non deleted operator.
UPDATE 2
Sure enough, this whole thing works if I both change the signature AND stop it from being an assingment operator. If I use the following function instead:
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 in side main.cpp
do:
d.assignmentOperator(dd);
everything works, there are no compile errors, see the selected answer for why my original seg-faulted. I'll be posting a new question to figure out how I can get around these nasty semantics...