简体   繁体   中英

Global overload of operator<< does not work, why?

I have learnt the operator<< can be overloaded by making it a friend function of class. For example,

struct Test
{
    std::string d_data;
    Test(const std::string & data) : d_data{data} {}
    friend std::ostream & operator<<(std::ostream & ostr, const Test & obj)
    {
        ostr << obj.d_data << '\n';
        return ostr;
    }
};

int main()
{
    Test t1("one");
    std::cout << t1;
    Test t2("two");
    std::cout << t2;
}
 one two

This seems to work as expected.

But, I'm unable to understand why the same isn't working for a global overload.

#include <iostream>
#include <ostream>
#include <string>

std::ostream & operator<<(std::ostream & os, const std::string & s)
{
    os << s << '\n';
    return os;
}

int main()
{
    std::cout << "stackoverflow";
    std::cout << "stackoverflow";
}

stackoverflowstackoverflow

Expected the strings to be separated by a newline, but didn't work as expected.

Your operator using

std::cout << "stackoverflow";

requires a user-defined conversion from an object of the type const char * (after the implicit conversion of the string literal to pointer to its first character) to an object of the type std::string .

However the standard basic_ostream class has already an operator that does not require such a conversion

template<class charT, class traits>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&, const char*);

So this operator is called instead of your operator.

Moreover within your operator

std::ostream & operator<<(std::ostream & os, const std::string & s)
{
    os << s << '\n';
    return os;
}

there is recursive calls of itself.

Your could define your operator the following way

#include <iostream>
#include <string>

std::ostream & operator<<(std::ostream & os, const char *s)
{
    return std::operator <<( os, s ) << '\n';
}

int main()
{
    std::cout << "stackoverflow";
    std::cout << "stackoverflow";
}

and get the expected result

stackoverflow
stackoverflow

Note that "stackoverflow" is of type const char[] , but not std::string . That means your overload won't be invoked, but the one from standard library ( operator<<(std::basic_ostream) is invoked, because it's an exact match and doesn't require the implicit conversion from const char[] to std::string .

 template< class Traits > basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os, const char* s );

BTW: It could be found because of ADL .

You can overload globally, but "stackoverflow" is not a std::string , so yours isn't used.
(And there already is such an overload in the standard library.)

To see that it works, move your first overload out of the class definition and make it a non-friend.
The only reason it has to be declared friend is that you have declared it inside the class definition, so it would be a member function otherwise.

This will work as you expect:

struct Test
{
    std::string d_data;
    Test(const std::string & data) : d_data{data} {}
};

std::ostream & operator<<(std::ostream & ostr, const Test & obj)
{
    ostr << obj.d_data << '\n';
    return ostr;
}

int main()
{
    Test t1("one");
    std::cout << t1;
    Test t2("two");
    std::cout << t2;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM