[解决]我在这里吹牛是不准确的,问题与编译有关。

我的代码是:

#include <iostream>
using namespace std;

struct Circle {
    void write(ostream& os) const {
        os<<*this;
    }
};

ostream& operator<<(ostream& os, const Circle& rhs_c) {
    os<<"circle";
    return os;
}

int main() {
    Circle c1;
    c1.write(cout);
    return 0;
}


我收到以下错误:

C2679 binary'<<': no operator found which takes a right-hand operand of type 'Circle' (or there is no acceptible conversion)


而如果全局运算符<
no operator "<<" matches these operands


我有这样一种扭曲的打印功能,可能会弄乱为什么上位代码错误的原因。

最佳答案

问题是您正在尝试调用“尚不存在”的运算符。因为您在头文件中定义了write()函数,并且在调用时在类下方声明了operator <
要将其包括在重载集中,必须在类之前首先声明该函数。但是要声明它必须知道的有关类的函数,因此还必须声明它。它有点凌乱,听起来比原来更糟:

// 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(),而是在声明operator <
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文件中,请删除内联。)

我个人建议将实现移至.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";
}


通过这种方式进行拆分,您可以在标头中包含小得多的类,而在.cpp文件中仅添加一次。这也意味着.cpp文件中的所有函数都可以查看标头中的所有声明(因此,排序不再重要)。这样,您的标头就会变得更整洁,更易于阅读类接口,而.cpp文件包含的重实现细节仅被编译一次(而不是包含标头的任何地方)。

09-10 02:53
查看更多