Как создать оператор вставки потока «по умолчанию» в C ++? - PullRequest
6 голосов
/ 08 января 2010

У меня есть класс, похожий на boost :: any, в том смысле, что это шаблонный класс-контейнер.Я хотел бы иметь метод для записи содержащегося значения в строку.Однако, если содержащийся тип не предоставляет оператор вставки потока, я бы хотел, чтобы мой метод возвращал некоторую строку по умолчанию, а не компилировал.Ниже приведено подробное описание того, что я пытаюсь сделать:

namespace W {
    namespace hide {
        template <typename T>
        std::ostream& operator<<(std::ostream& out, const T& t) {
            return std::operator<<(out, typeid(T).name());
        }
    }

    template <typename T> struct C {

        T t_;

        std::string ToString() const {
            using namespace hide;
            std::ostringstream oss;
            oss << t_;
            return oss.str();
        }
    };
}

Это работает довольно хорошо, с некоторыми оговорками.Например, если я хочу фактически предоставить перегруженный оператор вставки для класса, то этот оператор должен либо находиться в том же пространстве имен, что и класс, либо он должен находиться в пространстве имен W, чтобы его можно было рассмотреть.

У него также есть проблемы с любым типом, который уже имеет не член std :: operator <<, например, char и std :: string.Если T относится к одному из этих типов, то строка <code>oss << t_ выше становится неоднозначной.Это можно обойти, добавив перегрузки для этих типов в пространство имен W, например:

std::ostream& operator << (std::ostream& out, const std::string& s) {
    return std::operator <<(out, s);
}

Мой вопрос: кто-нибудь нашел лучший метод, чем этот?Почему я должен добавить свои собственные перегрузки для таких вещей, как std :: string?Это все поддерживается в соответствии со стандартом, или я пользуюсь нестандартным поведением?(Я тестирую с g ++ 4.3.3)

1 Ответ

3 голосов
/ 08 января 2010

Ниже приведен код, который, как я помню, видел некоторое время назад в классе конструирования компилятора. Я думал, что это было особенно умно (если не «чисто»), поэтому я держался за это.

С http://www.cs.colorado.edu/~main/a++/tree.h

   // If data of type T can be printed with the usual << operator, then
   // print<T>(out, p) will interpret *p as a T object and print its
   // value to out.  Otherwise, a message is printed to out, indicating
   // that objects of type T are not printable.
   template<typename T> void print(std::ostream& out, const void* p)
    {
    // The first part of this code sets an enum value, is_printable, to
    // be 1 if the data type T can be printed with the usual <<
    // operator.  Otherwise, is_printable is set to zero.  The programming
    // technique is based on a note from Herb Sutter at
    // http://www.gotw.ca/gotw/071.htm
    class object
    {
    public:
        object(T convert) { };
    };
    char operator << (std::ostream&, const object&);
    enum { is_printable = sizeof(std::cout << (*static_cast<T*>(0))) == sizeof(char) ? 0 : 1 };

        // Notice that the boolean expression in the if-statement is known at
    // compile time, so that only one of the two output statements will be
    // compiled into object code.
    if (is_printable)
        out << *static_cast<const T*>(p);
    else
        out << "(value of type " << typeid(T).name() << " cannot be printed)";
    }

Когда вы создаете свой контейнерный объект, удерживайте указатель на функцию печати для переменной:

void (*printer)(std::ostream&, const void*); 
printer = print<T>;

Затем позже используйте функцию принтера (), чтобы отобразить содержащееся значение, если это возможно.

Надеюсь, это поможет.

...