如何正确超载<<ostream的操作符?

我正在用c++写一个用于矩阵操作的小矩阵库。然而,我的编译器抱怨,以前它没有。这段代码被搁置了6个月,在此期间,我把我的电脑从debian etch升级到lenny (g++ (debian 4.3.2-1.1) 4.3.2 ),但是我在Ubuntu系统上遇到了同样的问题,使用相同的g++.

这是我的矩阵类的相关部分:

namespace Math
{
class Matrix
{
public:


[...]


friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
}
}

而“执行”:

using namespace Math;


std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {


[...]


}

这是编译器给出的错误:

matrix.cpp:459: error: 'std::ostream& 数学::矩阵::operator< & lt; (std:: ostream& const Math::Matrix&)'必须取 正好一个参数

我对这个错误有点困惑,但在这6个月做了很多Java之后,我的c++已经有点生疏了。: -)

434055 次浏览

你已经将你的函数声明为friend。它不是这个类的成员。你应该从实现中删除Matrix::friend表示指定的函数(它不是类的成员)可以访问私有成员变量。你实现这个函数的方式就像Matrix类的实例方法,这是错误的。

再加上迈赫达的回答,

namespace Math
{
class Matrix
{
public:


[...]




}
std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

在你的实现中

std::ostream& operator<<(std::ostream& stream,
const Math::Matrix& matrix) {
matrix.print(stream); //assuming you define print for matrix
return stream;
}

只是告诉你另一种可能性:我喜欢使用朋友的定义:

namespace Math
{
class Matrix
{
public:


[...]


friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
[...]
}
};
}

函数将自动定位到周围的命名空间Math(即使它的定义出现在该类的作用域内),但除非调用operator<<使用一个矩阵对象,它将使参数依赖查找找到操作符定义。这有时可以帮助处理模棱两可的调用,因为它对于矩阵以外的参数类型是不可见的。在编写它的定义时,你也可以直接引用Matrix中定义的名称和Matrix本身,而不需要用一些可能很长的前缀限定名称,并提供像Math::Matrix<TypeA, N>这样的模板参数。

假设我们讨论的是从std::ostream派生的所有类重载operator <<来处理Matrix类(而不是为Matrix类重载<<),在头文件的Math命名空间之外声明重载函数更有意义。

只有在无法通过公共接口实现该功能时,才使用好友函数。

Matrix.h

namespace Math {
class Matrix {
//...
};
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

注意,操作符重载是在名称空间之外声明的。

Matrix.cpp

using namespace Math;
using namespace std;


ostream& operator<< (ostream& os, const Matrix& obj) {
os << obj.getXYZ() << obj.getABC() << '\n';
return os;
}

另一方面,如果你的重载函数需要成为友元,即需要访问私有和受保护的成员。

Math.h

namespace Math {
class Matrix {
public:
friend std::ostream& operator<<(std::ostream&, const Matrix&);
};
}

你需要用命名空间块来封装函数定义,而不仅仅是using namespace Math;

Matrix.cpp

using namespace Math;
using namespace std;


namespace Math {
ostream& operator<<(ostream& os, const Matrix& obj) {
os << obj.XYZ << obj.ABC << '\n';
return os;
}
}

在c++ 14中,你可以使用下面的模板来打印任何具有T::print(std::ostream&)const;成员。

template<class T>
auto operator<<(std::ostream& os, T const & t) -> decltype(t.print(os), os)
{
t.print(os);
return os;
}

在c++ 20中可以使用概念。

template<typename T>
concept Printable = requires(std::ostream& os, T const & t) {
{ t.print(os) };
};


template<Printable T>
std::ostream& operator<<(std::ostream& os, const T& t) {
t.print(os);
return os;
}

我想用一个重载<<来打印数组的例子来简化一下。

  1. 首先将这两个对象类型传递给<<操作符
  2. 创建一个函数来重载操作符,如下所示。
#include<iostream>
using namespace std;


void operator<<(ostream& os, int arr[]) {
for (int i = 0;i < 10;i++) {
os << arr[i] << " ";
}
os << endl;
}
    

int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << arr;
}

如果操作符也需要级联,请确保返回cout对象 在重载函数中,

. 0
#include<iostream>
using namespace std;


ostream& operator<<(ostream& os, int arr[]) {
for (int i = 0;i < 10;i++) {
cout << arr[i] << " ";
}
cout << endl;
return os;
}
    

int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 11,22,33,44,55,66,77,88,99,100 };
// cascading of operators
cout << arr << arr2;
}