Оператор перегрузки << (без знака char typedef в виде байта)

Я хочу перегрузить (hijack?) ostream и basic_ostream<unsigned char>, чтобы он не пытался отображать октет (unsigned char) в качестве печатаемого символа.

Я живу с cout, и друзья слишком долго накладывают смайлики на экран. И мне надоело работать с кастами: hex << int(0xFF & b) << ....

Можно ли переопределить стандартное поведение? Я пробовал переопределение шаблонов и шаблонов. Они компилируются, но не отображаются.

Ответ 1

Проблема в том, что уже существует

template<class charT, class traits>
std::basic_ostream<charT,traits>& 
operator<<(std::basic_ostream<charT,traits>&, charT);

в namespace std. Поскольку basic_ostream<> также находится в этом пространстве имен, ADL выбирает его при выводе unsigned char. Добавление собственной перегрузки может привести к неоднозначности вызова оператора, или ваша перегрузка будет игнорироваться.

Но даже если бы это сработало, это было бы хрупким, потому что забвение одного из них могло бы тонко изменить смысл кода без какой-либо диагностики от компилятора.
И там еще: каждый программник-программист, смотрящий на такой код, будет предполагать, что стандартный оператор называется (и никогда не думает о добавлении include, когда он добавляет в код еще один вывод).
Короче говоря, лучше всего добавить функцию, выполняя то, что вы хотите сделать.

Разумной семантической альтернативой этому может быть добавление манипулятора потока, который вызывает нужный формат вывода. Я не уверен, что это технически возможно.

Ответ 2

Люк прав.

Быстрая альтернатива вашему текущему подходу — если вы не возражаете о десятичном выходе — заключается в продвижении char в int:

unsigned char c = '!';
os << +c;

Я не понимаю, как это будет облагаться налогом!

Ответ 3

#include <iostream>
#include <string>       // std::char_traits

typedef unsigned char       UChar;
typedef UChar               Byte;

typedef std::char_traits<char>      CharTraits;
typedef std::char_traits<wchar_t>   WCharTraits;

typedef std::basic_ostream< char, CharTraits >      CharOStream;
typedef std::basic_ostream< wchar_t, WCharTraits >  WCharOStream;

CharOStream& operator<<( CharOStream& stream, UChar v )
{
    return stream << v+0;
}

int main()
{
    char const      c   = 'c';
    UChar const     u   = 'u';

    std::cout << c << '\n' << u << std::endl;
}

Это хорошо работает с MSVC 10.0 и MinGW g++ 4.4.1, и он компилируется с помощью Comeau Online, поэтому я считаю, что это формально нормально.

Приветствия и hth.,

Ответ 4

Als прав, что то, о чем вы просите, не произойдет.

Лучшее, что вы можете сделать, это написать свой собственный IO-манипулятор (iomanip), чтобы сделать магию для вас. В этом случае, вам нужна функция, которая принимает unsigned char (хотя я настоятельно рекомендую использовать uint8_t из <stdint.h>).

#include <stdint.h>
#include <ostream>

class asHex
{
public:
    asHex(uint8_t theByte): value(theByte) {}
    void operator()(std::ostream &out) const 
        { std::ios::fmtflags oldFlags = out.flags; out << std::hex 
              << std::setw(2) << std::setfill('0') << std::uppercase << theByte; 
          out.flags(oldFlags); }
private:
    uint8_t theByte;
};

std::ostream& operator<<(std::ostream &out, asHex number)
{
    number(out); return out;
}

Затем вы можете написать:

cout << asHex(myByte);

Вы можете добавить конструкторы в asHex или даже сделать его классом шаблона для поддержки 16, 32 и других счетчиков бит.

(Да, я знаю, что <stdint.h> не является официальным заголовком С++, но я бы предпочел иметь его определения в глобальном пространстве имен вместо std:: без необходимости делать using namespace std;, который сбрасывает все в глобальном пространстве имен.)

Ответ 5

В связи с ADL будет вызываться стандарт operator<<. Попробуйте явно указать свой вызов:

::operator<<(os, 42);

Ответ 6

Вы не можете напрямую переопределить поведение std::cout. Это будет слишком подвержено ошибкам, если какой-либо код разработчика может изменить поведение стандартной библиотеки, используемой другим кодом.

Вы можете создать свой собственный класс, который эмулирует поведение std::cout и вместо этого использует этот объект.

class SpecialCout
{
    template <typename T>
    friend SpecialCout& operator<< ( SpecialCout const& scout, T const &t )
    {
        // Do any adjustments to t here, or decide to return early.

        std::cout << t;
        return *this;
    }

};

extern SpecialCout scout;