ARTICLE AD BOX
You're expecting your two versions of operator= to be treated as two overloads of equal precedence. [Un]fortunately, that only happens in the same scope; derived's scope incorporates base's indirectly, but it's not the same scope. When you define any operator= in derived, regardless of its parameter type(s), the base implementation is shadowed/hidden.
To unshadow it, you need to explicitly bring it into the same scope, with using base::operator=;, which explicitly brings all of the base implementation(s) of operator= into the same scope, and therefore makes them behave as you expect (though causing problems if two overloads have the same parameters).
class derived : public base { public: ... using base::operator=; // <-- add this derived& operator=(const int& x) { ... } };This problem is not unique to operator overloads, to be clear. You'd see the same problem with:
#include <iostream> #include <string> struct A { void print(int) { std::cout << "int\n"; } }; struct B: public A { void print(std::string) { std::cout << "string\n"; } }; int main() { B b; b.print("foo"); // Works, implicitly converts to std::string b.print(1); // Breaks, A::print(int) is hidden by B::print(std::string), no // overload available that can anything int can implicitly convert to }As a side-note: You're trying to implement these operators in isolation, but it's fiendishly difficult to get this correct, performant, and exception-safe without relying on some useful patterns, e.g.
the copy-and-swap idiom to make implementing operator= correct, performant, and exception-safe with minimal code duplication other general idioms for overloadingThese patterns dramatically reduce code duplication, improve performance, reduce errors, handle scoping issues, etc. I strongly recommend reading those links.
