Рассмотрите следующий минимальный пример:
#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;
}
};
Однако это приводит ко всем видам неоднозначностей.
rvalues не могут быть связаны с неконстантной ссылкой. Поэтому в вашем примере временный тип ostream не может быть первым аргументом свободного operator<<(std::ostream&, char const*), а используется член operator<<(void*).
Если вам это нужно, вы можете добавить вызов типа
myostream(cout).flush() << "foo";
который преобразует rvalue в ссылку.
Обратите внимание, что в C++0X введение rvalue reference позволит обеспечить перегрузку operator<<, принимающего rvalue references в качестве параметра, что решит первопричину проблемы.
Если у объекта нет имени (т.е. он временный), его нельзя привязать к неконстантной ссылке. В частности, он не может быть связан с первым параметром:
operator<<(ostream &, char *)
Я только что понял часть ответа. Временное значение не является lvalue, поэтому его нельзя использовать в качестве аргумента типа ostream &
.
Остается вопрос «как я могу это сделать?» ...
Что ж, я не знаю спецификации C ++, которая вызывает это, но легко понять, почему это происходит.
Временный объект находится в стеке, обычно для передачи другой функции или для вызова одной операции. Итак, если вы вызовете для него свободный оператор:
operator << (myostream (cout))
Он уничтожается в конце этой операции, и второй оператор «<<» для добавления endl будет ссылаться на недопустимый объект. Значение, возвращаемое свободным оператором «<<», будет ссылкой на разрушенный временный объект. Спецификация 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
Поскольку ни один из ответов до сих пор не дал чистого решения, я остановлюсь на грязном решении:
myostream operator<<(myostream stream, char const *str) {
std::operator<<(stream, str);
return stream;
}
Это возможно только потому, что myostream
имеет конструктор копирования. (Внутри он поддерживается std::stringbuf
с реф-счетом)