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?
CodePudding user response:
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.