Порядок разрешения перегрузки оператора с временными - PullRequest
11 голосов
/ 11 февраля 2010

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

#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 *) вместо оператора free operator<<(ostream &, char *). Кажется, имеет значение, имеет ли объект имя.

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

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

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

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

Ответы [ 6 ]

7 голосов
/ 11 февраля 2010

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

operator<<(ostream &, char *)
6 голосов
/ 11 февраля 2010

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

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

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

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

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

3 голосов
/ 11 февраля 2010

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

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

1 голос
/ 12 февраля 2010

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

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

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

0 голосов
/ 16 сентября 2015

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

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

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;
        }
};
0 голосов
/ 11 февраля 2010

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

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

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

Он уничтожается в конце этой операции, и второй оператор «<<» для добавления конца указывает на недопустимый объект. Возвращаемое значение из свободного оператора «<<» будет ссылкой на разрушенный временный объект. Спецификация C ++, вероятно, определяет правила для свободных операторов, чтобы этот сценарий не расстраивал и не сбивал с толку программистов C ++. </p>

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

1012 * редактировать *

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

#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
...