CRTP & copy/move assignment/constructor inheritance - c++

I am trying to implement a move/copy assignment operators and constructors in a base class for the derived classes using CRTP.
template <typename Derived>
class base {
public:
Derived& operator= (const Derived& other) {
// Copy the base properties:
this->foo_ = other.foo_;
// ...
// Continue as the derived class demands it:
this->derived().copy(other);
return this->derived();
}
// etc. for copy/move assignment/construction...
private:
// To act as the derived class:
Derived& derived () { return *static_cast<Derived*>(this); }
const Derived& derived () const { return *static_cast<const Derived*>(this); }
protected:
// Some base properties:
int foo_;
// ...
};
class derived: public base<derived> {
friend base<derived>;
public:
// Inheriting the constructors and assignment operators:
using base<derived>::base;
using base<derived>::operator=;
private:
void copy (const derived& other) {
// Copy all the needed derived properties:
this->bar_ = other.bar_;
// ...
}
// Some derived properties:
int bar_;
// ...
};
// To test it:
int main () {
derived d, t;
d = t;
}
Compiler gives me an error, saying that derived& derived::operator=(const derived&) cannot be overwritten with derived& base<derived>::operator=(const derived&). My theory is, that somehow derived::operator= gets defined implicitly and then by introducing the base<derived>::operator= by the using declaration I'm trying to redefine it again maybe? This looks suspiciously similar to errors that come up when accidentally defining a method twice.
I compiled this with GCC and the full log is:
test.cpp: In function 'int main()':
test.cpp:25:7: error: 'constexpr derived& derived::operator=(const derived&)' cannot be overloaded
class derived: public base<derived> {
^~~~~~~
test.cpp:4:14: error: with 'Derived& base<Derived>::operator=(const Derived&) [with Derived = derived]'
Derived& operator= (const Derived& other) {
^~~~~~~~
Is this even possible to accomplish, or do I have to define the operators/constructors in the derived class and then delegate their functionality to the base class inside the definition?
Update
OK, maybe after looking at this with a clearer mind, it seems overly complicated. I could just do the following:
Derived& base<Derived>::operator= (const base& other) {
this->foo_ = other.foo_;
return this->self();
}
So the returned type is correct for every derived class and the copy is performed from the base class - only the base properties are copied, which is all I need by default. If I need more, then it's specific to each derived class:
// Adding this to the base class - for any derived class to act as the base one:
template <Derived>
base<Derived>& base<Derived>::base () { *return static_cast<base<Derived>*>(this); }
derived& derived::operator= (const derived& other) {
this->base() = other.base();
this->bar_ = other.bar_;
}
But still, it's an interesting excercise and the question regarding the compiler error remains unanswered.

You can’t usefully declare a “derived operator=” with the usual signature in a base class because, even with a using-declaration, it is always hidden by the implicitly-declared copy assignment operator. (You could use some other signature for one or both of them, but then overload resolution is likely to be …interesting.)
Meanwhile, you’ve found a GCC bug in that it incorrectly concludes that the two operators conflict rather than one hiding the other.

Related

Inheritance, Copy Constructors and Implicit Typecasting

I have a derived class which I want to be able to construct using the copy constructor where the argument is an instance of the base class.
I am sure this should be possible in C++. Here is an example:
#include <string>
class Base
{
public:
friend
void swap(Base& l, Base& r)
{
using std::swap;
swap(l.a, r.a);
}
Base()
: a{1}
{
}
Base(const int a)
: a{a}
{
}
virtual
~Base()
{
}
Base(const Base& base)
: a{base.a}
{
}
Base(Base&& base)
: Base()
{
swap(*this, base);
}
Base& operator=(Base base)
{
swap(*this, base);
return *this;
}
protected:
int a;
};
class Derived : public Base
{
protected:
std::string b;
};
int main()
{
Base base(2);
Derived derived(base);
}
The error (g++ main.cpp) is:
main.cpp: In function ‘int main()’:
main.cpp:71:31: error: no matching function for call to ‘Derived::Derived(Base&)’
class Derived derived(base);
^
main.cpp:57:7: note: candidate: Derived::Derived()
class Derived : public Base
^~~~~~~
main.cpp:57:7: note: candidate expects 0 arguments, 1 provided
main.cpp:57:7: note: candidate: Derived::Derived(const Derived&)
main.cpp:57:7: note: no known conversion for argument 1 from ‘Base’ to ‘const Derived&’
main.cpp:57:7: note: candidate: Derived::Derived(Derived&&)
main.cpp:57:7: note: no known conversion for argument 1 from ‘Base’ to ‘Derived&&’
So the compiler doesn't know how to convert from an instance of Base to Derived implicitly.
I thought that this should be legal in C++. Do I require an explicit conversion statement?
What are you doing doesn't make much sense by itself because Base is not a sub-type of Derived so it can't be used as its replacement/substitution, however you could attempt to give it sense (same as with initialization from any other type) by writing a converting constructor:
class Derived : public Base
{
public:
Derived(const Base &bs) : Base(bs), b("constructed from base") {}
protected:
std::string b;
};
This would first initialize Derived Base part from bs and then init the string b with some value (though you can leave it out if you want it to be default-inited to empty string).
https://godbolt.org/z/GMELW_
Yes, you need to cast explicitely from Base to Derived. Every Mercedes is a car, but not every car is a Mercedes.
this statement Derived derived(base); or to simplify B b(A()); do an implicit conversion of type A to type B, which is legal only if class B inherit directly or indirectly from Class A.
Why ? Because class B could contains new information, in your case a string b, and a cast don't "append" information.
There is a simple way of solving this problem: By pulling the Base constructor into the scope of Derived.
This can be done with the using statement:
class Derived : public Base
{
public:
using Base::Base; // Pulls in the Base class constructors into the scope of Derived
...
};
I found what I was looking for (I couldn't remember the name so couldn't google it before). However this technique actually doesn't work due to the fact that I am using inheritance. (Or at least I don't know how to make it work.)
Type conversion operator:
Base::operator Derived()
{
Derived derived;
derived.a = a;
return derived;
}
This doesn't actually compile because the compiler doesn't know what Derived is. (Since Derived inherits from Base.) I don't know if it is possible to make this work by separating the compilation units.

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

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...
Derived &operator=(BaseCRTP rhs)
This assignment operator slices rhs to the type BaseCRTP<Derived> when the original argument's type was Derived. That's where the members are "lost."
You can make the operator accept BaseCRTP && and instead of using it in Derived, implement operator= there:
Derived& Derived::operator= (Derived rhs)
{
return BaseCRTP::operator= (std::move(rhs));
}

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&)|
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.
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;
CRTP is a choice:
template<typename swappable>
struct copy_swap_crtp{
auto& operator=(copy_swap_crtp const& rhs){
if (this==std::addressof(tmp))
return *this;
swappable tmp{rhs.self()};
self().swap(tmp);
return *this;
};
auto& operator=(copy_swap_crtp && rhs){
self().swap(rhs.self());
return *this;
};
protected:
auto& self(){return *static_cast<swappable*>(this);};
//auto& self()const{return *static_cast<swappable const*>(this);};
};
user class:
struct copy_swap_class
: copy_swap_crtp<copy_swap_class>
{
copy_swap_class(copy_swap_class const&);
void swap(copy_swap_class&);
};
cheers,
FM.
The compiler is right. The class A is abstract class, therefore you can not create instances of it in the operator=.
In B, you just declared the print function, but you didn't implement it. Meaning, you will get linking errors.
By implementing it, it compiles fine (if we ignore various warnings) :
void B::print(ostream & os )
{
os << m_att;
}
by the way :
B inherits privately from A, is that what you wanted?
order of initialization in A's copy constructor is wrong
you initialized m_type in A's constructor's body and not in the initialization list

Overloading operator+

I'm looking for an elegant solution to the following "problem":
Consider the classes Base and Child, and the way operator+ works here:
class Base
{
public:
Base(int a=0) : mValue(a) {};
Base(const Base& rhs) : mValue(rhs.mValue) {};
Base& operator=(const Base& rhs) {
if (this==&rhs) return *this;
mValue=rhs.mValue;
}
friend const Base operator+(Base &lhs, Base &rhs);
private:
int mValue;
};
const Base operator+(Base &lhs, Base &rhs)
{
Base result(lhs.mValue+rhs.mValue);
return result;
}
class Child : public Base
{
public:
Child(int a=0) : Base(a) {};
};
int main()
{
Child a(2);
Child b(5);
Child c(a+b); // **** This line ****
Child d;
d=(a+b); // **** or this other one ****
}
The marked lines in main give the error:
cannot convert from 'const Base' to 'Child'
I understand perfectly that the operator has been defined in the Base class, and returns an object of type Base, which can't be converted to Child.
One solution is overloading operator+ for the Child class, but I am wondering whether there is a better, less costly method. I'm under the impression that I'm forgetting a much easier option. Thanks!
you can define a constructor Child(Base& obj) then Child c=(a+b); statement will be fine and then you can use the base object as per your requirement.
There is no easier option.
Operator overloading and class hierarchies don't really like to mix. I'm personally very suspicious when a type that should be a value type (or else why are you overloading operators?) is part of a hierarchy. Can you describe your actual architecture? Does it actually make sense to overload the operator there?
If you want to build a child from a base without adding the appropriate constructor, there is another alternative. You can declare the casting operator in your base class, returning your mValue :
operator int() const;
This way, the compiler will do the job implicitly.
PS : Ensure const-correctness of your additive operator :
friend Base operator+(const Base &lhs, const Base &rhs);

Overloading baseclass assignment operator in subclass class leads to ambiguous assignment error

I have this base class (details removed)
template<class T>
class GPtr
{
public:
typedef T BaseType;
GPtr& operator=(const BaseType& rhs)
{
m_p = rhs.get();
return *this;
}
private:
BaseType m_p;
};
Then a sub-class specialises the template and adds another assignment option:
class GDrawablePtr : public GPtr<XYZ>
{
public:
GDrawablePtr& operator=(const RootType& rhs)
{
GPtr::operator =(convert<BaseType::element_type>(rhs));
return *this;
}
/* -- only compiles if this is uncommented
GDrawablePtr& operator=(const BaseType& rhs)
{
GPtr::operator =(rhs);
return *this;
}
*/
};
With that code commented out, I get compilation errors about ambiguous assignment when assigning instances. If I uncomment it, then even though it doesn't appear to do anything new, compilation is successful.
Is there a way to avoid re-defining the original base assignment operator, and what is the reason for this behaviour?
It's known as hiding: declaring a function in a derived class makes any function in the base class with the same name inaccessible. You can use a using-declaration to make the base class versions available too:
// In GDrawablePtr
using GPtr::operator=;