3

cout works with string (a.k.a. basic_string<char>) and all number types (int, char, unsigned char, double, etc.). However it cannot handle basic_string<unsigned char>.

#include <iostream>
#include <string>
int main()
{
    std::basic_string<unsigned char> zzz(3, 'z');
    std::cout << zzz << std::endl;
    return 0;
}

This doesn't compile with

error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::basic_string<unsigned char>')

I would expect this to behave the same way as string. Is there a reason why ostream doesn't handle std::basic_string<unsigned char>?

martinkunev
  • 1,364
  • 18
  • 39

4 Answers4

2

The standard defines the following template operator:

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);

This means you can stream std::string to std::cout, and std::wstring to std::wcout. You can't stream std::wstring to std::cout.

Your problem is that std::cout uses character type char, not unsigned char.

You can define an additional operator in the global namespace if you like.

typedef std::basic_string<unsigned char> ustring;

std::ostream& operator << (std::ostream& stream, const ustring& str) {
    if (const auto len = str.size())
       stream.write(reinterpret_cast<const char*>(&str[0]), len);
    return stream;
}    
martinkunev
  • 1,364
  • 18
  • 39
2

There is no matching std::ostream overload for it in the standard library. You can however, provide your own overload. Though you may have different behavior for characters that aren't std::isprint

#include <iostream>
#include <string>

std::ostream& operator << (std::ostream& os, const std::basic_string<unsigned char>& str){
    for(auto ch : str)
        os << static_cast<char>(ch);
    return os;
}

int main()
{
    std::basic_string<unsigned char> zzz(3, 'z');
    std::cout << zzz << std::endl;
    return 0;
}

Prints:

zzz

Demo

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
1

Like with, say, a std::vector<char>, there's no obvious "default" behaviour here, so it's not provided out of the box. It can work; you'll just have to define how it works yourself.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

The global object std::cout is of type std::ostream which is an alias for std::basic_ostream<char>. The relevant operator is std::basic_string::operator<< which requires that the std::basic_string<CharT> and the std::basic_ostream<CharT> share the same type CharT. In your example, you are using std::basic_string<unsigned char> and std::basic_ostream<char> which is not supported, since unsigned char and char are distinct types. Note that signed char and unsigned char are always distinct from char.

char - type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type). [...] Link.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87