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

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

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:错误:` std::ostream& 数学:矩阵::操作符< < (std::上ostream, const Math::Matrix&)'必须取 只有一个参数

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


当前回答

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

首先将两个对象类型传递给<<操作符 创建一个函数来重载操作符,如下所示。

#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对象 在重载函数中,

#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;
}

其他回答

假设我们讨论的是对std::ostream派生的所有类重载操作符<<来处理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&);
    };
}

您需要将函数定义包含在一个名称空间块中,而不是仅仅使用名称空间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;
    }                 
}

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

首先将两个对象类型传递给<<操作符 创建一个函数来重载操作符,如下所示。

#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对象 在重载函数中,

#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;
}

你已经表明了你作为朋友的职责。它不是这个类的成员。您应该从实现中删除Matrix::。Friend表示指定的函数(不是类的成员)可以访问私有成员变量。你实现函数的方式就像一个矩阵类的实例方法,这是错误的。

在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; 
} 

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

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

The function will be automatically targeted into the surrounding namespace Math (even though its definition appears within the scope of that class) but will not be visible unless you call operator<< with a Matrix object which will make argument dependent lookup find that operator definition. That can sometimes help with ambiguous calls, since it's invisible for argument types other than Matrix. When writing its definition, you can also refer directly to names defined in Matrix and to Matrix itself, without qualifying the name with some possibly long prefix and providing template parameters like Math::Matrix<TypeA, N>.