В Qt имеется перегруженный оператор << для разных контейнеров: QVector, QList и т.д. Например, можно написать так:<br>QVector vec vec << QString("text1") << QString ("text2") <br>Для STL-контейнеров также можно нечто подобное реализовать. К примеру, я такой написал код для std::vector template std::vector& operator<< (std::vector<T>& vec, const E& elem) { vec.push_back (elem) return vec } Можно писать так: std::vector vec vec << 54234 << 998 <br>Или даже так: std::vector vec vec << std::string ("word 1") << "word 2" <br>Удобство подобной штуки очевидно. Как Вы думаете, какие могут быть в этом подходе подводные камни? (Я имею в виду общую идею, а не мой конкретный код, хотя если найдете в нем ошибки или способ его улучшить, буду очень благодарен)
Ответ Синтаксис удобный, но подводные камни все-таки есть.
Рассмотрим следующий код. Понятно, что в момент (1) в контейнере будут храниться 3 строчки, и это всех устраивает. QVector v v.append("Морж1") v.append("Морж2") v.append("Морж3") // (1)
Дальше программист Иван замечает, что этот код можно упростить c помощью крутого синтаксиса: QVector v = QVector() << "Морж1" << "Морж2" << "Морж3" <br> ^
После этого приходит тимлид Илларион и видит, что в месте, отмеченном галочкой, выполняется ненужное копирование объекта (предположим, что Илларион не очень в курсе про copy-on-write). Зато Илларион в курсе про использование константных ссылок в похожих случаях - он знает, что по стандарту ([C++ Standard] - 10.4.10 Temporary Objects), если константная ссылка инициализируется временной переменной, то время жизни этой переменной становится равным времени жизни ссылки. Опираясь на свое знание стандарта, самоуверенный Илларион достаточно часто пишет такой код, и у него все прекрасно работает: const QVector& ref = QVector(3, "Морж") Q_ASSERT(ref.size() == 3)
И по этой же причине он не особо задумывается, когда правит код Ивана на следующий и нажимает кнопочку commit: const QVector& v = QVector() << "Морж1" << "Морж2" << "Морж3" <br> Бабах!
Иллариону приходит гневное письмо от системы Continuous Integration о том, что его коммит сломал несколько тестов. А ему еще повезло, что этот код был ими покрыт! А дело тут вот в чем (это реализация operator<< для QVector): inline QVector<T>& operator<<(const T &t) { append(t) return *this }<br> Как можно заметить, последняя из операций (<<) возвращает ссылку (QVector<T>&) на созданную чуть раньше временную переменную QVector. Естественно, что никто в таком случае не будет продлевать время жизни этой переменной - последняя операция << при синтаксическом разборе не имеет (и не нуждается в) информации о том, где и как была эта переменная была создана.<br> Технически говоря, это rvalue, из которой путем неявного преобразования мы получили lvalue. Если подытожить, то const QVector& v - висячая ссылка, соответствующий ей объект уже уничтожен, а Илларион мог бы лишиться премии, если бы не писал тесты. Вот такая вот история.