How can I properly overload the << operator for an ostream? [duplicate]
Asked Answered
S

6

286

I am writing a small matrix library in C++ for matrix operations. However, my compiler complains, where before it did not. This code was left on a shelf for six months and in between I upgraded my computer from Debian 4.0 (Etch) to Debian 5.0 (Lenny) (g++ (Debian 4.3.2-1.1) 4.3.2). However, I have the same problem on a Ubuntu system with the same g++.

Here is the relevant part of my matrix class:

namespace Math
{
    class Matrix
    {
    public:

        [...]

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

And the "implementation":

using namespace Math;

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

    [...]

}

This is the error given by the compiler:

matrix.cpp:459: error: 'std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' must take exactly one argument

I'm a bit confused by this error, but then again my C++ has gotten a bit rusty after doing lots of Java those six months. :-)

Stank answered 24/1, 2009 at 16:34 Comment(0)
B
145

You have declared your function as friend. It's not a member of the class. You should remove Matrix:: from the implementation. friend means that the specified function (which is not a member of the class) can access private member variables. The way you implemented the function is like an instance method for Matrix class which is wrong.

Bookstack answered 24/1, 2009 at 16:37 Comment(4)
And you should also declare it inside the Math namespace (not just with an using namespace Math).Sapphirine
Why does the operator<< have to be in the namespace of Math? It seems that it should be in the global namespace. I agree that my compiler wants it to be in the namespace of Math, but that doesn't make sense to me.Westland
Sorry, but I fail to see why do we use friend keyword here then? When declare friend operator override in a class, it seems like we cannot implement with Matrix::operator<<(ostream& os, const Matrix& m). Instead we need to just use global operator override operator<<ostream& os, const Matrix& m) so why even bother to declare it inside class in the first place?Angular
Patrick, You would use friend so that the implementation has access to private (and protected) member variables. E.g. It's likely you would want to print all the elements of the matrix, which may be private.Bobbinet
P
158

I am just telling you about one other possibility: I like using friend definitions for that:

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>.

Plenteous answered 24/1, 2009 at 22:28 Comment(0)
B
145

You have declared your function as friend. It's not a member of the class. You should remove Matrix:: from the implementation. friend means that the specified function (which is not a member of the class) can access private member variables. The way you implemented the function is like an instance method for Matrix class which is wrong.

Bookstack answered 24/1, 2009 at 16:37 Comment(4)
And you should also declare it inside the Math namespace (not just with an using namespace Math).Sapphirine
Why does the operator<< have to be in the namespace of Math? It seems that it should be in the global namespace. I agree that my compiler wants it to be in the namespace of Math, but that doesn't make sense to me.Westland
Sorry, but I fail to see why do we use friend keyword here then? When declare friend operator override in a class, it seems like we cannot implement with Matrix::operator<<(ostream& os, const Matrix& m). Instead we need to just use global operator override operator<<ostream& os, const Matrix& m) so why even bother to declare it inside class in the first place?Angular
Patrick, You would use friend so that the implementation has access to private (and protected) member variables. E.g. It's likely you would want to print all the elements of the matrix, which may be private.Bobbinet
L
94

To add to Mehrdad's answer,

namespace Math
{
    class Matrix
    {
       public:

       [...]


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

In your implementation

std::ostream& operator<<(std::ostream& stream,
                     const Math::Matrix& matrix) {
    matrix.print(stream); // Assuming you define print for matrix
    return stream;
 }
Laktasic answered 24/1, 2009 at 18:51 Comment(4)
I don't understand why is this a down vote, this clarifies that you can declare operator to be in the namespace and not even as a friend and how you can possibly declare the operator.Laktasic
Mehrdad answer did not have any snippet of code so I just added what might work by moving it outside the class in the namespace itself.Laktasic
I understand your point, I only looked at your second snippet. But now I see you took the operator out of the class. Thanks for the suggestion.Stank
Not only it is out of the class, but it is properly defined inside the Math namespace. Also it has the added advantage (maybe not for a Matrix, but with other classes) that 'print' can be virtual and thus printing will happen at the most derived level of inheritance.Sapphirine
P
81

Assuming that we're talking about overloading operator << for all classes derived from std::ostream to handle the Matrix class (and not overloading << for Matrix class), it makes more sense to declare the overload function outside the Math namespace in the header.

Use a friend function only if the functionality cannot be achieved via the public interfaces.

Matrix.h

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

Note that the operator overload is declared outside the namespace.

Matrix.cpp

using namespace Math;
using namespace std;

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

On the other hand, if your overload function does need to be made a friend i.e. needs access to private and protected members.

Math.h

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

You need to enclose the function definition with a namespace block instead of just 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;
    }                 
}
Putdown answered 10/2, 2012 at 16:9 Comment(2)
Just a little nitpicking here.. i find os to be a poor abbreviation in this case (it is bound too much to "operating system")Canvas
Can the operator<< overload be put (declared and implemented) inside the class, and we don't talk about friendship?Stalwart
D
49

In C++14, you can use the following template to print any object which has a T::print(std::ostream&)const; member.

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

In C++20, concepts can be used.

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;
}
Decomposed answered 5/3, 2016 at 8:24 Comment(5)
interesting solution! One question - where this operator should be declared, like in a global scope? I assume it should be visible to all types which can be used to templatize it?Viradis
@Viradis It could be in your own namespace along with the classes that use it.Decomposed
can't you just return std::ostream&, since it's the return type anyway ?Artefact
@Jean-MichaëlCelerier The decltype makes sure that this operator is only used when t::print is present. Otherwise it would attempt to compile the function body and give a compilation error.Decomposed
Concepts version added, tested here godbolt.org/z/u9fGbKDecomposed
U
4

I would like to simplify this a little with an example that overloads << to print an array.

  1. First pass both the object types around the << operator

  2. Create a function to overload the operator as follows.

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

If cascading of operators is also required, make sure to return a cout object in the overloaded function as follows,

#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;
}
Underpay answered 27/5, 2021 at 17:23 Comment(2)
You can make it work with any size array:- template<int N> ostream& operator<<(ostream& os, int(& arr)[N]) { etcDecomposed
Shouldn't be os instead of cout inside the operator== function?Hesperidin

© 2022 - 2024 — McMap. All rights reserved.