没有运算符<<发现虽然有一个定义,但它采用类Foo类型的右手操作数

时间:2018-04-27 21:53:59

标签: c++ oop operator-overloading

[已解决]我在这里吹嘘一些不准确,问题与编译有关。

我的代码是:

#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)

而如果全球运营商&lt;&lt;实际上有些东西与我得到的操作数不匹配:

no operator "<<" matches these operands

我有理由让这种扭曲的打印功能可能会破坏为什么上面的代码是错误的。

2 个答案:

答案 0 :(得分:3)

问题在于您正在尝试呼叫“尚不存在”的运营商。因为你在头文件中定义了write()函数,并声明了运算符&lt;&lt;在类的下方,在调用时,就编译器而言,运算符&lt;&lt;不包含在重载集中。

要将它包含在重载集中,必须先在类之前声明该函数。但要声明它必须知道的关于类的函数,所以你还必须声明它。这是混乱的,听起来比它更糟糕:

// 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(),而是在运算符&lt;&lt;被宣布。也许将定义移动到.cpp文件,但您也可以在类之外实现它以获得相同的效果:

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文件包含较重的实现细节,只编译一次(而不是包含你的标题的所有地方。)

答案 1 :(得分:2)

当你这样做时:

os<<*this;

运算符重载尚未发生,并且确切地说,编译器不会被告知它将在稍后的代码中发生。

转发声明操作符重载的原型,它告诉编译器你,程序员,承诺为这个重载提供定义。

然后编译器会在遇到此行(os<<*this;)时知道该怎么做。

但它不知道Circle是什么,对吧?所以你还需要转发声明课程&#39;声明也是。

此外,您可能忘记在运营商的超载中返回您的信息流。

将所有内容放在一起,并使用public范围来简化(但请阅读friend范围以了解运算符重载here),您得到:

#include <iostream>
using namespace std;

class Circle;
ostream& operator<<(ostream& os, const Circle& rhs_c);

class Circle {
    public:
    void write(ostream& os) const {
        os<<*this;
    }
};

ostream& operator<<(ostream& os, const Circle& rhs_c) {
    // param 'rhs_c' should be used of course
    os<<"circle";
    return os;
}

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