3

I have stumbled upon an error for which I cannot grasp the reason.

I think it basically gets down to this error:

 error: no matching function for call to ‘std::basic_ostream<char>::operator<<(const std::basic_string<char>&)’

I looked into specification on www.cplusplus.com and indeed it says there is no definition for std::ostream::operator<< with std::string as an argument.

My question is, what happens when one writes std_ostream_instance << std_fancy_string;. I believe it is one of the most common invocations ( e.g. std::out << std::string("Hello world!") ) next to const char*.

The error originates from these lines:

template<typename T> 
void Log::_log(const T& msg)
{  _sink->operator<<( msg ); }

_sink is defied as std::ostream* There are some wrapping functions around but it breaks here.

I think I could work around by writing

template<> 
void Log::_log<std::string>(const std::string& msg) {
  _sink->operator<<( msg.c_str() );
}

since there is ostream& operator<< (ostream& out, const unsigned char* s ); defined by default.

I just see no reason why it is not guessed automatically since it clearly works in simple use like cout << any_std_string.

Not sure if this is relevant but I want to be able to pass down through my log functions anything than can be handled by std::ostream. I used explicit non-templated declarations but decided to move to template for log(const T& anything_to_log) to refacator it. It seemed plain stupid to have 5+ overloads. I get the error when I try compiling something like Log::log( std::string("test case") ).

It looks like something stupid-simple but I cannot get it on my own. Tried to google and search stack to no avail.

With regards, luk32.

PS. I checked the work-around and it works. Why it's not implicitly done ?

luk32
  • 15,812
  • 38
  • 62
  • 1
    `std::string` has the `.c_str()` member function which converts to `const char*`. Doesn't that work? – Tony The Lion Sep 14 '12 at 12:54
  • 2
    http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt Maybe you could use something like `(*_sink) << some_arg` to pick up both member and non-member versions. – BoBTFish Sep 14 '12 at 12:55

2 Answers2

9

operator << overloads are not members of ostream. They are freestanding functions, for example

ostream& operator << ( ostream& out, const basic_string<T>& bs );

Try

template<typename T> 
void Log::_log(const T& msg)
{  *_sink << msg;  }
hamstergene
  • 24,039
  • 5
  • 57
  • 72
  • Worked. Thank you. I thought that `ostream::operator<<()` would pick up globally defined versions. My fault. My problem was similar to such case `operator<< (*_sink, msg);`, where in turn compiler can't see definitions for `const char*` invocations. Your version picks up both. – luk32 Sep 14 '12 at 13:20
5

The std::string version is not a member function, so can't be called as a member of _sink. Try it this way to pick up both member and non-member versions (in fact it is unlikely you will need the member versions at all anyway):

#include <iostream>
#include <string>

int main()
{
    std::ostream * os = &std::cout;
    std::string s = "Hello\n";

    // This will not work
    // os->operator<<(s);
    (*os) << s;

    return 0;
}

Or better yet would be to store _sink as a reference, and output exactly as you normally would to cout.

BoBTFish
  • 19,167
  • 3
  • 49
  • 76
  • Thank you. Also good reference in the comment under the question. Another good example to your code would be `char* s2= "test2"; operator<< ( *os, s2);` which would fail. Only `(*os) << s` will pass both. hamstergene was 1st to answer so I accepted his. But this has good examples. – luk32 Sep 14 '12 at 13:22