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);
Related
I've been working on some class hierarchy with a goal to have a collection of classes that encapsulate primitive types (AdvancedInt, AdvanceDouble, etc.) and give them some additional behavior when accessing their underlaying values, through conversion operator and assignment operator. I can't do this with getters and setter as I can't change the original code and the way it access them, so my only choice is operator overloading.
The problem I ran into is with the templated class that defines the conversion operator which gives me a compiler error.
Here is the code that illustrates the problem
#include <string>
class Base {
public:
Base() = delete;
Base(std::string s) : name(s) {
//Some extra logic
}
~Base() {
//More custom logic
}
virtual std::string get_name() const final { //Final on purpose
return name;
}
private:
const std::string name;
};
template <typename T, class C> class Serializable :public Base { //Poor man's interface (but not quite)
public:
Serializable() = delete;
Serializable(std::string name) : Base(name) {
}
operator T() const {
//Some extra 'getter' logic
return value;
}
C& operator=(const T& val) {
//Some extra 'setter' logic
value = val;
return (C&)*this;
}
virtual void serialize() = 0; //This has to be pure virtual
private:
T value;
};
class AdvancedInt final : public Serializable<int, AdvancedInt> { //This is the actual complete class
public:
AdvancedInt(std::string name) : Serializable(name) {
//Nothing here, but needed for the special constructor logic from AbstractBase
}
void serialize() override {
//Some logic here
}
};
int main()
{
AdvancedInt adv{"foo"};
int calc = adv;
calc += 7;
adv = calc; //error C2679 (msvc) | binary '=': no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
return 0;
}
Now aside from the (probably) questionable practice of passing AdvancedInt to the template of its own base class and it subsequently knowing that it's part of some derived class and implementing its logic (although any feedback on this is welcome), my main question is, what am I doing wrong here?
The C& operator=(const T& val) compiles fine when defined as pure virtual, but that enforces the implementation to be in the derived class, and when implemented there with T and C replaced by their corresponding types it works just fine, so the signature is logically sound, but probably not in the correct place(?). From what I've found on cppreference.com, the simple assignment operator is the only assignment operator that cannot be defined outside of class definition. Is this the reason why this code doesn't compile? And if so, is declaring it as pure virtual a way to go?
With adv = calc we look for operator= from AdvancedInt and found (implicit):
AdvancedInt& operator=(const AdvancedInt&);
AdvancedInt& operator=(AdvancedInt&&);
and look-up stops there.
Adding:
using Serializable<int, AdvancedInt>::operator=;
would allow to consider also that overload.
And that fixes your issue.
Demo.
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.
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
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=;
My problem is this, I have a base class from which classes are derived. I have an overloaded += operator in the base class but I only want to be able to add derived classes of the same type. I do this by returning the lhs and producing a warning message if they are not the same type.
class base{
int variable;
public:
base(int v);
base & operator+=(const base & rhs);
class a: public base{
}
class b: public base{
}
base & operator+=(const base & rhs){
if(type_same(const & rhs){
variable+=rhs.variable; }
return *this
}
a first(1);
a second(5);
b third(2);
first+=second // first.variable now =6
second+=third // warning produced second.variable still = 5
I've never used dynamic cast before so my question is is it possible/advisable to a check using it and typeid to find out whether the types passed are the same. My alternative is a string constant name for each derived class.
Why have the operator+ in the base class in the first place? If you just place operator+ with the correct type into the a and b class, it will work, without any dynamic_cast.
dynamic_cast is usually a hint that your types are not well designed and should be avoided. In the described scenario, you can surely get by without any dynamic_cast. If not, then a and b should not be derived from base, as they do behave differently. Think about it from the user perspective: By deriving a from base, you tell the user that a can be used as base. If a method requires the actual type, move it down instead of putting it into the base interface.
The correct usage btw. would be to dynamic_cast the rhs to the current type (i.e. dynamic_cast<b*>(&rhs)) and if that returns a non-zero value, then rhs was indeed of the correct type (however, think about what happens if there is a class derived from b being passed in!)
To solve the problem that you stated in your post, dynamic_cast and RTTI are definitely the right tools. However, issuing a warning at runtime while providing compile-time support for a '+' operation may point to issues in the design where the users of your API would not expect exactly the behavior that your API provides.
My take: you could employ CRTP
The below code will issue the type mismatch error at compile time, as suggested by others.
template <typename Derived>
class base{
int variable;
public:
base(int v) { variable = v; }
Derived& operator+=(const Derived& rhs);
};
struct a: base<a> {
a(int v): base<a>(v) {}
};
struct b: base<b> {
b(int v): base<b>(v) {}
};
template <typename Derived>
Derived& base<Derived>::operator+=(const Derived& rhs)
{
variable += rhs.variable;
return static_cast<Derived&>(*this);
}
int main(int argc, const char *argv[])
{
a a1(1), a2(2);
b b1(1), b2(2);
a1 += a2;
b1 += a2; // compile time error :)
return 0;
}
Even if you really wanted to type the operator arguments as base& you'd at least gain the benefit of using static_cast<> because the conversion is exact and well defined at runtime. I can expand the idea in a few