Проблема в том, что вы пытаетесь вызвать оператора, который "еще не существует". Поскольку вы определили свою функцию write () в заголовке и объявили оператор << ниже класса, в точке вызова, что касается компилятора, оператор << не включен в набор перегрузки. </p>
Чтобы включить его в набор перегрузки, вы должны сначала объявить функцию перед вашим классом. Но чтобы объявить функцию, она должна знать о классе, поэтому вы также должны объявить это. Это грязно и звучит хуже, чем это:
// 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";
}
Таким образом, когда оператор вызывается в write (), компилятор узнает, что оператор существует, а затем он будет определен и компоновщик выполнит вызов.
Существует и другой способ - НЕ реализовывать write () в определении класса, но после объявления оператора <<. Возможно, переместите определение в файл .cpp, но вы также можете реализовать его вне класса для того же эффекта: </p>
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;
}
В обоих случаях используется одно и то же: определение write () физически располагается после объявления оператора, который он ожидает использовать.
(Если он помещен в файл .cpp, удалите inline .)
Лично я рекомендую переместить реализацию в файл .cpp. Вы не только избегаете подобных проблем, вы также можете избежать включения в свой заголовок, и это большой файл (десятки тысяч строк кода для компиляции каждый раз, когда ваш заголовок включен.) Вместо этого вы должны использовать только «вперед» объявления к классам iostream доступны из заголовка, и тогда вы можете обойтись без этого шаблона:
// header file
#include <iosfwd>
#pragma once
class Circle {
void write(std::ostream&) const;
};
std::ostream& operator<<(std::ostream& os, Circle const& circle);
А потом
// .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";
}
Разделив его таким образом, вы включаете намного меньший класс в свой заголовок, а heave - только один раз в свой файл .cpp. Это также означает, что все функции в файле .cpp видят все объявления в заголовке (поэтому порядок больше не имеет значения). Тогда ваш заголовок станет чище, легче для понимания интерфейса класса, в то время как файл .cpp содержит более сложные детали реализации, которые компилируются только один раз (вместо того, чтобы везде включать ваш заголовок.)