C ++ перегрузка - PullRequest
       26

C ++ перегрузка

1 голос
/ 10 октября 2010

Следующий код дает мне ошибку компиляции.Может кто-нибудь сказать, пожалуйста, почему?

class mytype {
public:
    int value;
    mytype(int a) {
        value = a;
    }
    friend ostream& operator<<(ostream& stream, const mytype& a) {
        stream << a.value;//works
        return stream;
    }
    friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
        stream << (a.value);//compilation error
        return stream;
    }
};

Ошибка:

Ошибка C2027: использование неопределенного типа 'std :: basic_ostringstream <_Elem, _Traits, _Alloc>'

После исправления:

ошибка C2666: 'оператор <<': 18 перегрузок имеют аналогичные преобразования </p>

Окончательное исправление:

Объявите конструктор как явный.Тогда работает на MSVC.

Интересно, почему.

Ответы [ 2 ]

7 голосов
/ 10 октября 2010

ошибка C2027: использование неопределенного типа 'std :: basic_ostringstream <_Elem, _Traits, _Alloc>'

Вам нужно #include <sstream>, чтобы получить [i/o]stringstream классы.

О других ошибках

Проблема с перегрузкой формы

ostringstream& operator<<(ostringstream& stream, const mytype& a)

в том, что оно соответствует ostringstream точно . Почему плохо иметь более точную перегрузку? Из стандарта, §13.3.3 / 1-2:

жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов i ICSi (F1) не хуже последовательности преобразования, чем ICSi (F2)…

Если существует ровно одна жизнеспособная функция, которая лучше, чем все другие жизнеспособные функции, то это один выбранный по разрешению перегрузки; в противном случае вызов неверен

Поэтому, если operator<<( ostream &, int ) и operator<<( ostringstream &, mytype const & ) оба являются кандидатами на stream << value, первое совпадение int точно не имеет преобразования, а второе совпадение ostringstream точно. Поэтому ни один не может быть «не хуже» для всех аргументов, и ни один из кандидатов не может быть выбран.

Но код является действительным из-за лазейки. Ваша перегрузка вообще не подходит, кроме случаев, когда вы фактически используете свой тип в вызове функции. Когда вы объявляете / определяете friend внутри блока класса, он не вводит его ни в какую область имен; он просто связывает его с областью действия класса, что позволяет найти его, если этот тип класса описывает один из передаваемых аргументов.

В стандарте говорится об объявлениях друзей, хотя в другом разделе (14.6.5):

Объявления друзей не вводят новые имена ни в одну сферу ...

Итак, MSVC постарался быть милым и активно представил вашего друга окружающему пространству имен. Удар по MSVC.

Однако, когда я попытался добавить объявление, эквивалентное тому, что MSVC делал «бесплатно», Comeau и GCC приятно решили возникший конфликт перегрузки - нанес им удар. Либо это? Как оказалось, перегруженный вызов происходит в файле раньше, чем мое рекомендуемое объявление. Если я перенесу декларацию до class mytype { (для чего требуется декларация форварда mytype), то оба будут правильно жаловаться на неоднозначность.

Использование перегрузки до того, как она будет объявлена ​​в области имен, представляется целесообразной в соответствии с §3.3-3.4 Стандарта. Так что на самом деле GCC и Comeau были правы. Точка декларации находится сразу после названия объявленного объекта. (И последнее, что я проверил, объявления самоссылочной функции могут по-прежнему вызывать сбой GCC .) ADL вызывает неквалифицированный поиск во вложенном пространстве имен в точке непосредственно перед включающим классом. (3.4.1 / 8, последний пункт, 3.4.1 / 9, 3.4.2 / 2a.) Если друг не был объявлен перед классом, он на законных основаниях не является кандидатом. (7.3.1.2/3) Разве C ++ не является красивым языком?

Как сохранить упрощенный пример в GCC, но нарушить последующий код.

    friend ostringstream& operator<<(ostringstream& stream, const mytype& a) {
        stream << (a.value);//compilation error
        return stream;
    }
};

ostringstream& operator<<(ostringstream& stream, const mytype& a); // <- here

После этого объявления невозможно будет записать int в ostringstream.

Как все равномерно разбить, с более простой семантикой объявления.

class mytype; // <- here
              // and here:
inline ostringstream& operator<<(ostringstream& stream, const mytype& a);

class mytype {
public:

После этого объявления будет невозможно записать int в ostringstream… включая объявления друзей внутри class mytype {}.

Фактическое решение.

Предполагается, что потоковые классы неразличимы. Если вы действительно хотите определить, подает ли данный поток строку в память (а вам не следует), лучше взглянуть на его внутренний объект streambuf, возвращаемый rdbuf(), который фактически выполняет ворчание ввода-вывода. Даже общий объект ostream может иметь функциональность ostringstream, если ему дано stringbuf.

 if ( typeid( stream.rdbuf() ) == typeid( stringbuf * ) ) {
     // this is effectively a stringstream
 } else {
     // not a stringstream
 }
2 голосов
/ 10 октября 2010

Существует неоднозначность перегрузки при вызове operator<< при перегрузке ostringstream.

stream << a.value;

Существует ряд перегрузок operator<<, которые являются членами класса ostream, который является базовым классом ostringstream. Один из них объявлен как:

ostream& ostream::operator<<(int);

Эта перегрузка является точным соответствием для правой стороны (a.value - это int), но для левой стороны требуется преобразование из производного в основание (stream - это ostringstream, который получен из ostream).

Однако есть и ваша перегрузка ostringstream:

ostringstream& operator<<(ostringstream&, const mytype&);

Эта перегрузка является точным соответствием для левой стороны (stream - это ostringstream), и определяемый пользователем конструктор преобразования (ваш mytype(int) конструктор) может использоваться для преобразования a.value ( int) до mytype.

Поскольку одна перегрузка лучше соответствует первому аргументу, а другая перегрузка лучше соответствует второму аргументу, возникает неоднозначность. Вы можете исправить это либо:

  • Явное преобразование левой части в ostream (с использованием (ostream&)stream = a.value;) или
  • Удалите пользовательское преобразование, сделав конструктор explicit.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...