Подтвердить что ты не робот

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

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

#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 С++,

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;
        }
};

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

4b9b3361

Ответ 1

rvalues ​​не могут быть привязаны к неконстантной ссылке. Таким образом, в вашем примере временный тип ostream не может быть первым аргументом свободного оператора < (ltd:: ostream &, char const *), и то, что используется, является оператором-членом < < (void *).

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

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

который преобразует значение r в ссылку.

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

Ответ 2

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

operator<<(ostream &, char *)

Ответ 3

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

Вопрос "как я могу сделать эту работу" остается...

Ответ 4

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

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

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

Ответ 5

В то время как С++ 11 разрешает эту проблему, поскольку есть ссылки rvalue, я думаю, что это может быть обходным путем для pre-С++ 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;
        }
};

Ответ 6

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

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

оператор < < (myostream (соиЬ))

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

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

изменить

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

#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