Порядок разрешения перегрузки оператора с участием временных

StackOverflow https://stackoverflow.com/questions/2245780

Вопрос

Рассмотрим следующий минимальный пример:

#include <iostream>

using namespace std;

class myostream : public ostream {
    public:
        myostream(ostream const &other) :
            ostream(other.rdbuf())
        { }
};

int main() {
    cout << "hello world" << endl;

    myostream s(cout);
    s << "hello world" << endl;

    myostream(cout) << "hello world" << endl;
}

Выходные данные, как на g ++, так и на Visual C ++, являются

hello world
hello world
0x4012a4

Версия, которая записывает во временный объект, myostream(cout), по - видимому , предпочитает оператор - член ostream::operator<<(void *), вместо свободного оператора operator<<(ostream &, char *).Кажется, имеет значение, есть ли у объекта имя или нет.

Почему это происходит?И как мне предотвратить такое поведение?

Редактировать:Почему это происходит, теперь ясно из различных ответов.Что касается того, как предотвратить это, следующее кажется привлекательным:

class myostream : public ostream {
    public:
        // ...
        myostream &operator<<(char const *str) {
            std::operator<<(*this, str);
            return *this;
        }
};

Однако это приводит ко всевозможным двусмысленностям.

Это было полезно?

Решение

значения r не могут быть привязаны к неконстантной ссылке.Итак, в вашем примере временный тип ostream не может быть первым аргументом free operator<<(std::ostream&, char const*) и используется оператор-член<<(недействительно*).

Если вам это нужно, вы можете добавить вызов, такой как

myostream(cout).flush() << "foo";

который преобразует rvalue в ссылку.

Обратите внимание, что в C ++ 0X введение ссылки на значение rvalue позволит обеспечить перегрузку оператора<< принимая ссылки на rvalue в качестве параметра, устраняем основную причину проблемы.

Другие советы

Если у объекта нет имени (т.е.это временное), оно не может быть привязано к неконстантной ссылке.В частности, он не может быть привязан к первому параметру:

operator<<(ostream &, char *)

Я только что понял часть от ответа.Временное значение не является значением lvalue, поэтому его нельзя использовать в качестве аргумента типа ostream &.

Вопрос "как я могу заставить это работать" остается...

Поскольку ни один из ответов пока, похоже, не дает чистого решения, я соглашусь на грязное решение:

myostream operator<<(myostream stream, char const *str) {
    std::operator<<(stream, str);
    return stream;
}

Это возможно только потому, что myostream имеет конструктор копирования.(Внутренне он подкреплен ссылкой, подсчитанной std::stringbuf.)

Хотя C ++ 11 действительно решает эту проблему, поскольку существуют ссылки на rvalue, я думаю, что это может быть обходным путем для версии, предшествующей C ++ 11.

Решение состоит в том, чтобы иметь функцию-член << оператор, в котором мы можем привести к неконстантной ссылке на базовый класс:

class myostream : public ostream {
    public:
        // ...
        template<typename T>
        ostream &operator<<(const T &t) {
            //now the first operand is no longer a temporary,
            //so the non-member operators will overload correctly
            return static_cast<ostream &>(*this) << t;
        }
};

Ну, я не знаю спецификацию C ++, которая вызывает это, но легко понять, почему это происходит.

Временный файл хранится в стеке, обычно для передачи другой функции или для вызова над ним одной операции.Итак, если вы позвоните бесплатному оператору по этому:

оператор<<(мой поток (cout))

Он уничтожается в конце этой операции и второй "<<" оператор для добавления endl будет ссылаться на недопустимый объект.Возвращаемое значение из бесплатного "<<"operator будет ссылкой на уничтоженный временный объект.Спецификация C ++, вероятно, определяет правила о свободных операторах, чтобы этот сценарий не расстраивал и не сбивал с толку программистов C ++.

Теперь, в случае с "<<(void*)" оператор-член во временном, возвращаемое значение - это сам объект, который все еще находится в стеке и не уничтожен, поэтому компилятор знает, что его не нужно уничтожать, а нужно передать следующему оператору-члену, тому, который принимает endl.Объединение операторов во временные цепочки является полезной функцией для сжатого кода C ++, поэтому я уверен, что разработчики спецификаций C ++ учли это и намеренно внедрили компилятор для его поддержки.

Редактировать

Некоторые говорили, что это связано с неконстантной ссылкой.Этот код компилируется:

#include <iostream>
using namespace std;
class myostream : public ostream { 
    public: 
        myostream(ostream const &other) : 
            ostream(other.rdbuf()) 
        { } 
            ~myostream() { cout << " destructing "; }
    }; 
int _tmain(int argc, _TCHAR* argv[])
{
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
    std::operator << (result, "illegal");
         return 0;
}

И это возвращается

  This works destructing illegal
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top