I'm having trouble calling a template assignment operator overload method, of a template base class. This is what I have so far:
BaseClass.h:
template<typename T>
class BaseClass
{
template<typename U>
BaseClass<T>& operator=(const BaseClass<U>& o)
{
return *this;
};
};
ChildClassInt.h:
#include "BaseClass.h"
class ChildClassInt : public BaseClass<int>
{
};
ChildClassFloat.h:
#include "BaseClass.h"
class ChildClassFloat : public BaseClass<float>
{
};
main.cpp:
#include "ChildClassInt.h"
#include "ChildClassFloat.h"
int main()
{
const ChildClassFloat floatClass;
ChildClassInt intClass;
return 0;
}
How do I call the operator=() method of intClass and pass it floatClass?
intClass always has an implicitly defined assignment operator, which always hides assignment operators from base classes. To explicitly call the base class assignment operator requires explicit qualification:
intClass.BaseClass<int>::operator=(floatClass);
(Note: Your code has two errors. First, BaseClass<int>::operator= is private. Second, it should return *this, not this.)
However, you probably should not do this. In general, assigning to a subobject can result in a broken complete object, since the base class assignment operator isn't aware of the derived class's invariants. If you really need to assign a ChildClassFloat to a ChildClassInt, you should define an assignment operator:
ChildClassInt& ChildClassInt::operator=(const ChildClassFloat& c) {
BaseClass<int>::operator=(c);
return *this;
}
Then the user can just do:
intClass = floatClass;
Since this assignment operator is part of the implementation of ChildClassInt, it knows how to perform the assignment in a way that preserves the invariants of ChildClassInt and will be updated if necessary when the implementation changes. The user, on the other hand, cannot be expected to know whether directly performing partial assignment to a base class will work, and, even if it does, the user code can be broken at any time by a change to the class implementation.
Related
Until a test I've just made, I believed that only Constructors were not inherited in C++. But apparently, the assignment operator= is not too...
What is the reason of that ?
Is there any workaround to inherit the assignment operator ?
Is it also the case for operator+=, operator-=, ... ?
Are all other functions (apart from constructors/operator=) inherited ?
In fact, I encountered this problem as I was doing some CRTP :
template<class Crtp> class Base
{
inline Crtp& operator=(const Base<Crtp>& rhs) {/*SOMETHING*/; return static_cast<Crtp&>(*this);}
};
class Derived1 : public Base<Derived1>
{
};
class Derived2 : public Base<Derived2>
{
};
Is there any solution to get that working ?
EDIT : OK, I have isolated the problem. Why the following isn't working ? How to solve the problem ?
#include <iostream>
#include <type_traits>
// Base class
template<template<typename, unsigned int> class CRTP, typename T, unsigned int N> class Base
{
// Cast to base
public:
inline Base<CRTP, T, N>& operator()()
{
return *this;
}
// Operator =
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline CRTP<T, N>& operator=(const T0& rhs)
{
for (unsigned int i = 0; i < N; ++i) {
_data[i] = rhs;
}
return static_cast<CRTP<T, N>&>(*this);
}
// Data members
protected:
T _data[N];
};
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
};
// Main
int main()
{
Derived<double, 3> x;
x() = 3; // <- This is OK
x = 3; // <- error: no match for 'operator=' in ' x=3 '
return 0;
}
The assignment operator is technically inherited; however, it is always hidden by an explicitly or implicitly defined assignment operator for the derived class (see comments below).
(13.5.3 Assignment) An assignment operator shall be implemented by a
non-static member function with exactly one parameter. Because a copy
assignment operator operator= is implicitly declared for a a class if
not declared by the user, a base class assignment operator is always
hidden by the copy assignment operator of the derived class.
You can implement a dummy assignment operator which simply forwards the call to the base class operator=, like this:
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline Derived& operator=(const T0& rhs)
{
return Base<Derived, T, N>::operator=(rhs);
}
};
The assignment operator is inherited, sort of, but... In any given
class, if you do not provide a copy assignment operator, the compiler
generates one for you. That means that your derived classes effectively
have an assignment operator:
Derived& operator=( Derived const& );
And the usual hiding rules apply; this hides all of the base class
assignment operators. (If the base class had an assignment operator
with this signature, the derived class would inherit it normally.)
Your assignment operator is technically inherited, but then it's hidden by the default copy assignment operator in the derived class. This default copy assignment then tries to call the base class's copy assignment which doesn't exist since you hid it with your own assignment.
The sanest way to resolve this is to not use operator overloading in non-obvious ways (= not meaning copy assignment for example). In this case, don't use operator=: Call it something like assign or set and then it will inherit and not be hidden by the child copy assignment.
These operators are inherited and there are no compiler versions so they will never be automatically hidden like operator=.
It really is only constructors that aren't inherited, and I can't think of any other compiler-generated functions that could hide something from the parent as in operator=.
I am reading qt sources and I've seen a code like this many times:
buttonOpt.QStyleOption::operator=(*opt);
So, I guess it is something like buttonOpt = *opt but why do they use this syntax instead of default and user-friendly? Is this faster or any other profit exists?
This is because they are explicitly calling the operator= from the base class of buttonOpt, which is QStyleOption.
buttonOpt.QStyleOption::operator=(*opt);
//similar behavior
class Base
{
public:
virtual bool operator<(Base & other)
{
std::cout << "Base";
}
};
class Derived : public Base
{
public:
bool operator<(Base & other) override
{
std::cout << "Derived";
}
};
int main()
{
Derived a;
Derived b;
a < b; //prints "Derived"
a.Base::operator <(b); //prints "Base"
}
The code you show is explicitly calling the base class assignment, i.e. only the base class parts of the QStyleOptionButton get assigned, but not the member variables of the object.
It appears from the documentation, that no operator= is declared for QStyleOptionButton, so if one would call the usual assignment on such an object, the compiler would try to generate such an operator, consisting of the assignment of each base class subobject and each member variable.
Such a generated operator may or may not compile, depending of whether all members and base classes are copyable. In such cases it is usual to define the operator manually, to do the assignment correctly, if the class should be copyable at all.
However, the probable reason to call the base class assignment explicitly is that indeed only the base class parts need to be copied, while the other class members should not be changed, so this is not a "real assignment" in the semantical sense.
I am overloading operator= on a struct EqualTestBase, and operator= takes different parameters than are used to construct the struct.
struct EqualTestBase
{
EqualTestBase(int one) {}
EqualTestBase& operator=(std::string two)
{
//stuff
return *this;
}
};
It works fine on the base class. But a trival struct derived from it, EqualTestDerived, acts like it doesn't have the operator= member function.
struct EqualTestDerived : public EqualTestBase
{
EqualTestDerived(int one) : EqualTestBase(one) {}
};
void test()
{
EqualTestBase basetest(0);
basetest = "test"; //this is fine, compiles
EqualTestDerived derivedtest(0);
derivedtest = "test"; //this does not compile, says there is no constructor that takes type const char[5]
}
Do I have to redefine operator= on all derived structs, or is there a way to automatically pass down that functionality?
The derived class has an implicitly-declared copy-assignment operator, which hides the one declared in the base class. You can use using to bring it into scope:
struct EqualTestDerived : public EqualTestBase
{
EqualTestDerived(int one) : EqualTestBase(one) {}
using EqualTestBase::operator=;
};
operator= isn't inherited. If a class doesn't define operator= itself, the compiler will synthesize one for it (regardless of the fact that its base class does define an operator=).
If you want something that can be inherited (and can be virtual) you generally want to define it as a function with a different name. The generally accepted name for such a function is clone (though clone is normally more like a copy constructor, creating a new instance of an object, not just assigning to an existing one).
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=;
Until a test I've just made, I believed that only Constructors were not inherited in C++. But apparently, the assignment operator= is not too...
What is the reason of that ?
Is there any workaround to inherit the assignment operator ?
Is it also the case for operator+=, operator-=, ... ?
Are all other functions (apart from constructors/operator=) inherited ?
In fact, I encountered this problem as I was doing some CRTP :
template<class Crtp> class Base
{
inline Crtp& operator=(const Base<Crtp>& rhs) {/*SOMETHING*/; return static_cast<Crtp&>(*this);}
};
class Derived1 : public Base<Derived1>
{
};
class Derived2 : public Base<Derived2>
{
};
Is there any solution to get that working ?
EDIT : OK, I have isolated the problem. Why the following isn't working ? How to solve the problem ?
#include <iostream>
#include <type_traits>
// Base class
template<template<typename, unsigned int> class CRTP, typename T, unsigned int N> class Base
{
// Cast to base
public:
inline Base<CRTP, T, N>& operator()()
{
return *this;
}
// Operator =
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline CRTP<T, N>& operator=(const T0& rhs)
{
for (unsigned int i = 0; i < N; ++i) {
_data[i] = rhs;
}
return static_cast<CRTP<T, N>&>(*this);
}
// Data members
protected:
T _data[N];
};
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
};
// Main
int main()
{
Derived<double, 3> x;
x() = 3; // <- This is OK
x = 3; // <- error: no match for 'operator=' in ' x=3 '
return 0;
}
The assignment operator is technically inherited; however, it is always hidden by an explicitly or implicitly defined assignment operator for the derived class (see comments below).
(13.5.3 Assignment) An assignment operator shall be implemented by a
non-static member function with exactly one parameter. Because a copy
assignment operator operator= is implicitly declared for a a class if
not declared by the user, a base class assignment operator is always
hidden by the copy assignment operator of the derived class.
You can implement a dummy assignment operator which simply forwards the call to the base class operator=, like this:
// Derived class
template<typename T, unsigned int N> class Derived : public Base<Derived, T, N>
{
public:
template<typename T0, class = typename std::enable_if<std::is_convertible<T0, T>::value>::type>
inline Derived& operator=(const T0& rhs)
{
return Base<Derived, T, N>::operator=(rhs);
}
};
The assignment operator is inherited, sort of, but... In any given
class, if you do not provide a copy assignment operator, the compiler
generates one for you. That means that your derived classes effectively
have an assignment operator:
Derived& operator=( Derived const& );
And the usual hiding rules apply; this hides all of the base class
assignment operators. (If the base class had an assignment operator
with this signature, the derived class would inherit it normally.)
Your assignment operator is technically inherited, but then it's hidden by the default copy assignment operator in the derived class. This default copy assignment then tries to call the base class's copy assignment which doesn't exist since you hid it with your own assignment.
The sanest way to resolve this is to not use operator overloading in non-obvious ways (= not meaning copy assignment for example). In this case, don't use operator=: Call it something like assign or set and then it will inherit and not be hidden by the child copy assignment.
These operators are inherited and there are no compiler versions so they will never be automatically hidden like operator=.
It really is only constructors that aren't inherited, and I can't think of any other compiler-generated functions that could hide something from the parent as in operator=.