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

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

Живой пример на Ideone (прекрасный пример неопределенного поведения. Ничего не печатает для меня на MSVC10).

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

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

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

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

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

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

1 Ответ

15 голосов
/ 12 января 2012

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

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

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

...