Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Using std string accessor with ostream operator <<

If I create a class:

// First Example
#include <iostream>
#include <string>

class my_class {
  std::string str;
public:
  my_class(const char* s = "") : str(s) {}

  operator const char* () const { return str.data(); } // accessor
};

my_class mc1{"abc"};
std::cout << mc1; // Calls the char* accessor and successfully writes "abc" to screen output.

If I modify the class thus:

// Second Example
class my_class {
  std::string str;
public:
  my_class(const char* s = "") : str(s) {}

  operator std::string () const { return str; } // accessor
};

my_class mc1{"abc"};
std::string mystring = mc1; // Calls the string accessor
std::cout << mystring; // Also successfully writes "abc" to screen output.

However, if I try to call:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

std::cout << mc1;

I will get a page full of compilation errors that begin with:

error C2679: binary ‘<<‘: no operator found which takes a right-hand operand of type ‘my_class’ (or there is no acceptable conversion)

I can correct this error by adding to the second example class:

  friend std::ostream& operator <<(std::ostream& os, my_class& rhs) {
    os << rhs.str;
    return os;
  }

which I mostly cribbed from one of the suggested solutions to this problem. But I don’t understand why it is necessary using a string accessor but not a char* accessor.

I was expecting successful compilation and output of the value of mc1.str, OR I would have expected the same error trying to use the char* accessor function in the fist example. Instead I received C2679 on the second example only.

>Solution :

This happens because of how the functions are defined. For the const char* case the function cout has for that is declared as

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

so when it analyzes std::cout << mc1; it can deduce what CharT and Traits from cout and it finds my_class::operator const char* () to convert mc1 into a const char* so overload resolution is successful and the code compiles.

When you switch to having operator std::string () and use std::cout << mc1; you now need to call the std::string overload for operator << which is declared as

template< class CharT, class Traits, class Allocator >
std::basic_ostream<CharT, Traits>&
    operator<<( std::basic_ostream<CharT, Traits>& os,
                const std::basic_string<CharT, Traits, Allocator>& str );

In this overload not only does the first parameter rely on the template parameters but so does the second parameter. This means the compiler is going to try and deduce the the types of CharT, Traits and Allocator from mc1 directly. No conversion operators are considered durning this step and since mc1 is not actually a std::string deduction fails and you are left with no possible overloads so the code fails to compile.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading