The problem is that you are trying to call an operator that "doesn't exist yet." Because you defined your write() function in the header, and declared the operator<< below the class, at the point of invocation, as far as the compiler is concerned, operator<< is not included in the overload set.
To include it in the overload set, you must declare the function first, before your class. But to declare the function it has to know about the class, so you must also declare that. It is messy-ish and sounds worse than it is:
// declare that Circle is a class, so the compiler will know what a reference to it means
class Circle;
// declare the operator, but not define it yet, since Circle is incomplete.
ostream& operator<<(ostream& os, const Circle& rhs_c);
class Circle {
void write(ostream& os) const {
os<<*this;
}
};
inline ostream& operator<<(ostream& os, const Circle& rhs_c) {
os<<"circle";
}
This way, when the operator is called in write(), the compiler knows the operator exists, and then later it is defined and the linker will make the call happen.
There is another way too, and that is to NOT implement write() in the class definition, but after the operator<< is declared. Perhaps move the definition to a .cpp file, but you could also implement it outside the class for the same effect:
class Circle {
void write(ostream& os) const;
};
inline ostream& operator<<(ostream& os, const Circle& rhs_c) {
os <<"circle";
}
inline void Circle::write(ostream& os) const {
return os << *this;
}
Both ways the same thing is at play here: the definition of write() is physically located after the declaration of the operator it expects to use.
(If placed in the .cpp file, remove inline.)
Personally I recommend moving the implementation to a .cpp file. Not only do you avoid these kinds of problems, you can also avoid including in your header, and it's a big file (tens of thousands of lines of code to compile anytime your header is included.) Instead, you should only use "forward" declarations to iostream classes available from the header, and then you can get away with this pattern:
// header file
#include <iosfwd>
#pragma once
class Circle {
void write(std::ostream&) const;
};
std::ostream& operator<<(std::ostream& os, Circle const& circle);
And then
// .cpp file
#include "Circle.hpp"
#include <iostream>
void Circle::write(std::ostream& os) {
os << *this;
}
std::ostream& operator<<(std::ostream& os, Circle const& circle) {
return os << "circle";
}
Splitting it this way, you include the much smaller class in your header, and the heave only once in your .cpp file. It also means that all the functions in the .cpp file see all the declarations in the header (so ordering no longer matters). Then your header is cleaner, easier to read class interface, while the .cpp file contains the heavier implementation detail that is only compiled once (instead of everywhere your header is included.)