257

我正在用 C++ 编写一个用于矩阵运算的小型矩阵库。但是我的编译器抱怨,以前没有。这段代码被搁置了 6 个月,在这期间我将计算机从 debian etch 升级到 lenny (g++ (Debian 4.3.2-1.1) 4.3.2 ) 但是我在具有相同 g++ 的 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: error: 'std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' 必须只取一个参数

我对这个错误有点困惑,但是在这 6 个月里做了很多 Java 之后,我的 C++ 又变得有点生疏了。:-)

4

6 回答 6

147

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

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

该函数将自动定位到周围的命名空间Math(即使它的定义出现在该类的范围内),但除非您使用 Matrix 对象调用 operator<< ,否则它将不可见,这将使依赖于参数的查找找到该运算符定义。这有时可以帮助模棱两可的调用,因为它对于 Matrix 以外的参数类型是不可见的。在编写其定义时,您还可以直接引用 Matrix 中定义的名称和 Matrix 本身,而无需使用一些可能很长的前缀来限定名称并提供模板参数,例如Math::Matrix<TypeA, N>.

于 2009-01-24T22:28:38.647 回答
138

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

于 2009-01-24T16:37:35.983 回答
87

要添加到 Mehrdad 的答案,

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;
 }
于 2009-01-24T18:51:16.277 回答
77

假设我们正在讨论对operator <<派生自std::ostream处理Matrix类的所有类进行重载(而不是<<Matrix类进行重载),那么在标头中的 Math 命名空间之外声明重载函数更有意义。

仅当无法通过公共接口实现功能时才使用友元功能。

矩阵.h

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

请注意,运算符重载是在命名空间之外声明的。

矩阵.cpp

using namespace Math;
using namespace std;

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

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

数学.h

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

您需要用命名空间块而不是仅仅将函数定义括起来using namespace Math;

矩阵.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}
于 2012-02-10T16:09:49.673 回答
46

在 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; 
} 
于 2016-03-05T08:24:36.600 回答
1

我想通过一个重载<<以打印数组的示例来简化这一点。

  1. <<首先在运算符周围传递两种对象类型
  2. 创建一个函数来重载运算符,如下所示。
#include<iostream> 
using namespace std;

void operator<<(ostream& os, int arr[]) {
    for (int i = 0;i < 10;i++) {
        cout << arr[i] << " ";
    }
    cout << 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;
}
于 2021-05-27T17:23:42.063 回答