Как правильно перегрузить оператор << для ostream? - PullRequest
217 голосов
/ 24 января 2009

Я пишу небольшую матричную библиотеку на 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: ошибка: 'std :: ostream & Math :: Matrix :: оператор << (станд :: ostream &, const Math :: Matrix &) 'должен взять ровно один аргумент </p>

Я немного смущен этой ошибкой, но опять же мой C ++ стал немного ржавым после того, как я много занимался Java за эти 6 месяцев. : -)

Ответы [ 5 ]

132 голосов
/ 25 января 2009

Просто скажу вам еще об одной возможности: мне нравится использовать для этого определения друзей:

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

Функция будет автоматически нацелена на окружающее пространство имен Math (даже если ее определение находится в области действия этого класса), но не будет видимой, если вы не вызовете operator << с объектом Matrix, который сделает поиск зависимым от аргумента найти это определение оператора. Иногда это может помочь с неоднозначными вызовами, поскольку он невидим для типов аргументов, отличных от Matrix. При написании его определения вы также можете напрямую ссылаться на имена, определенные в Matrix, и на саму Matrix, не квалифицируя имя с некоторым возможно длинным префиксом и предоставляя параметры шаблона, такие как <code>Math::Matrix<TypeA, N>.

118 голосов
/ 24 января 2009

Вы объявили свою функцию как friend. Это не член класса. Вы должны удалить Matrix:: из реализации. friend означает, что указанная функция (которая не является членом класса) может получить доступ к закрытым переменным-членам. То, как вы реализовали функцию, похоже на метод экземпляра для класса Matrix, что неверно.

74 голосов
/ 24 января 2009

Чтобы добавить в ответ 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;
 }
60 голосов
/ 10 февраля 2012

Предполагая, что мы говорим о перегрузке operator << для всех классов, производных от 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&);
    };
}

Вам нужно заключить определение функции в блок пространства имен, а не просто 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;
    }                 
}
31 голосов
/ 05 марта 2016

В C ++ 14 вы можете использовать следующий шаблон для печати любого объекта, который имеет T :: print (std :: ostream &) const; член.

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