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

Почему rvalue-перегрузка `operator <<` для `basic_ostream` возвращает ссылку lvalue?

§27.7.3.9 определяет следующую перегрузку для operator<<:

template <class charT, class traits, class T>
  basic_ostream<charT, traits>&
  operator<<(basic_ostream<charT, traits>&& os, const T& x);

Эффекты: os << x
Возвращает: os

(§27.7.2.6 определяет перегрузку rvalue для operator>>.)
В принципе, он просто переходит к перегрузке lvalue. Я считаю, что эта перегрузка является довольно опасной (istream еще одна, чем на самом деле ostream), рассмотрим следующее:

#include <sstream>
#include <iostream>

int main(){
  auto& s = (std::stringstream() << "hi there!\n");
  std::cout << s.rdbuf(); // oops
}

Пример Live на Ideone (идеальный пример поведения undefined). На MSVC10 ничего не печатает.

Приведенный выше пример может выглядеть ухищренным, но не должно быть слишком сложно попасть в эту ситуацию в общий код или при передаче (std::stringstream() << "text") функции, которая обеспечивает lvalue и rvalue-перегрузку и сохраняет std::ostream или std::istream по-разному в зависимости от перегрузки.

Теперь, что будет аргументом, снова возвращающим basic_ostream<charT, traits>&& и указав следующее?

Возвращает: move (os)

(И то же самое для basic_istream.)

Есть ли что-нибудь, что я пропускаю? В текущем состоянии, на мой взгляд, это выглядит просто опасно и как дефект. Я просмотрел список проблем LWG и нашел это предложение ( привет @HowardHinnant!). Он действительно возвращает rvalue, однако только для дополнительного преимущества того, что вы можете связать этого специального оператора, а не конкретно рассматривая проблему безопасности, описанную выше (хотя она, безусловно, ее разрешает). Кроме того, он обозначен как закрытый и для повторного рассмотрения для следующего стандарта. Таким образом, я подумал, что спрошу здесь:

Есть ли веская причина, почему вышеупомянутая перегрузка возвращает ссылку lvalue?

4b9b3361

Ответ 1

Это дефект, и это моя вина, извините. LWG 1203 (спасибо, что нашел это для меня!:-)) мое текущее мнение о правильном исправлении для "rvalue-stream-inserter". Обратите внимание, что вы все еще можете поймать его и оказаться в беде:

auto&& s = (std::stringstream() << "hi there!\n");
std::cout << s.rdbuf(); // oops

Хотя по крайней мере в приведенном выше коде немного более очевидно (из-за &&), что вы делаете что-то, вы не должны.