Relation between ostream insertion operator and it

2019-08-07 14:54发布

问题:

int i=0;
std::cout << i;

1, The non-member overload(two arguments, one being ostream& the other being type of operand, say int) is defined in std namespace. So if using namespace std; is not present, there's no way I see that the non-member overload can be called.

2, If instead, the member operator(member of ostream; one argument, being the type of operand, say int) function << of ostream is called, I'd expect something like: std::cout.operator<< i; --- isn't this rediculous?

So, what on earth is the relation between the two?

Question extended: if I want to overload the insertion operator so that std::cout << my_obj; works(assume my_obj is the instance of a user-defined type, MyType), should I do

a) ostream& operator<< (ostream& os, MyType obj); in the namespace of MyType

or

b) in MyType:

class MyType
{
 ...
   ostream& operator<< (MyType);
};

does option b) ever work? Does it even make sense(I remember seeing it somewhere ...)? What's the relation between option a) & b)?


EDIT MAR 27

Upon the request from ecatmur, here're two overloads that have the same signature but in different namespaces. Using gcc 4.9.2: http://melpon.org/wandbox/

#include <iostream>

namespace MyNameSpace
{
    class MyType
    {};

    // signature can be found here: http://www.cplusplus.com/reference/ostream/ostream/operator-free/
    std::ostream& operator<<(std::ostream& out, char ch)
    {
        out << "std is doomed\n";
        return out;
    }

    std::ostream& operator<<(std::ostream& out, MyType t)
    {
        out << "std is super doomed\n";
        return out;
    }
}

int main()
{
    MyNameSpace::MyType t;
    std::cout << t; // ... super doomed; ADL is working as intended.
    std::cout << 't'; // t; this proves that when there're two operator overloads with the same signature but in different namespaces, there're no compilation errors. gcc defaults to the std one.
    return 0;
}

回答1:

The non-member overloads you're thinking of are those for character data and those for std::string etc.; they're available via argument-dependent lookup. You can indeed write std::cout.operator<<(i) for types where ostream has a member operator<<.

For your own types you're thinking of a (non-member) friend operator<<, which can be particularly useful for template classes (the Barton-Nackman trick) or for those where the operator<< accesses data not available via accessors:

class MyType
{
 ...
   friend std::ostream& operator<< (std::ostream&, MyType obj) { ... }
};

Your proposed signature would allow you to write my_obj << my_obj, which is unlikely to make much sense.