Порядок разрешения перегрузки оператора с участием временных
-
20-09-2019 - |
Вопрос
Рассмотрим следующий минимальный пример:
#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