basic_ostream::operator<< неоднозначность символов между basic_ostream и пользовательским классом

#c

#c

Вопрос:

Рассмотрим этот пример:

 #include <sstream>
#include <ctime>

using MyStream = std::stringstream;

struct MyTime
{
    explicit MyTime(std::time_t time) // w/o explicit the MyTime operator << is ambigous as well
    {
    }
};

/*

having this overload of course solves everything, but that seems wrong

std::stringstream amp;operator<<(std::stringstream amp;stream, char c)
{
    stream.put(c);
    return stream;
}
*/

MyStreamamp; operator<<(MyStream amp;stream, std::time_t time)
{
    return stream;
}

MyStreamamp; operator<<(MyStream amp;stream, const MyTime amp;time)
{
    return stream;
}

int main(int argc, char **argv)
{
    MyStream stream;
    stream << 'Z';
    return 0;
}
 

Без std::stringstream amp;operator<<(std::stringstream amp;stream, char c) объявления stream << 'Z' сбой завершается с неопределенностью (поскольку для него нет перегрузки char , его можно повысить как до short , так и до hit basic_ostream , а также до time_t и hit MyStream ). Я действительно не хочу использовать basic_ostream::put ), поскольку цепочка из нескольких записей будет выглядеть уродливо (конечно, если это последнее средство, тогда …). Итак, это вызывает у меня два вопроса:

  1. В чем причина отсутствия char перегрузки basic_ostream::operator<< ? Это все сводится к кодировкам символов?
  2. Есть ли какие-либо хорошие решения (что-то, чего мне не хватает, кроме использования put или char перегрузки)?

Обновить

Таким образом, кажется, что наследование от std::stringstream vs using using устраняет проблему, но это ставит меня перед дилеммой идти против догмы «никогда не наследовать от классов stl».

Я предполагаю, что проблема возникает из-за того, что using на самом деле не вводит новый тип, поэтому двусмысленность:

Объявление псевдонима типа вводит имя, которое может использоваться как синоним типа, обозначаемого type-id . Он не вводит новый тип и не может изменить значение существующего имени типа.

Комментарии:

1. Есть выбор между вашим operator<<(std::stringstreamamp; stream, std::time_t time) и template <..>operator<<(basic_ostream<CharT,Traits>amp;, CharT) . Ни один не лучше.

2. Существует реализация для char , но это свободная функция , а не член. Я предполагаю, что он не выбирается, потому что он реализован на basic_ostream , а не на более конкретном stringstream — что-то, что вы могли бы сделать и для своего пользовательского типа.

3.Вы можете устранить проблему, внедрив перегрузку std::ostream вместо std::stringstream Demo .

4. @Thomas ах, спасибо, я пропустил бесплатную функцию. Хорошо, это дает больше понимания.

5. @Jarod42 Я подумал об этом, я вроде как хотел, чтобы после этого было легко извлекать строку, но да, я могу просто обернуть свой собственный буфер и жить с std::ostream ним.

Ответ №1:

Вы можете устранить проблему, реализовав перегрузку с std::ostream помощью вместо std::stringstream

 std::ostreamamp; operator<<(std::ostream amp;stream, std::time_t )
{
    return stream;
}

std::ostreamamp; operator<<(std::ostream amp;stream, const MyTime amp;)
{
    return stream;
}
 

ДЕМОНСТРАЦИЯ.

Комментарии:

1. Это решает проблему, хотя это лишает меня намерения иметь определенное поведение для моего точного типа потока, но я думаю, что нет лучших способов обойти это.

2. только что пришел к выводу, что на самом деле вывод (о чем все предупреждают для классов std) устраняет проблему. Извините, мне придется отказаться от этого, поскольку это сохраняет семантику, которая мне нужна. Но я не уверен, как на это влияют правила разрешения.