Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

operator<< для STL-контейнеров

#stl #qt #c++


В Qt имеется перегруженный оператор << для разных контейнеров: QVector, QList и т.д.
Например, можно написать так:
QVector vec;
vec << QString("text1") << QString ("text2");

Для STL-контейнеров также можно нечто подобное реализовать. К примеру, я такой написал
код для std::vector
template  std::vector& operator<< (std::vector& vec, const
E& elem) {
    vec.push_back (elem);
    return vec;
}

Можно писать так:
std::vector vec;
vec << 54234 << 998;

Или даже так:
std::vector vec;
vec << std::string ("word 1") << "word 2";

Удобство подобной штуки очевидно. Как Вы думаете, какие могут быть в этом подходе
подводные камни? (Я  имею в виду общую идею, а не мой конкретный код, хотя если найдете
в нем ошибки или способ его улучшить, буду очень благодарен)    


Ответы

Ответ 1



Синтаксис удобный, но подводные камни все-таки есть. Рассмотрим следующий код. Понятно, что в момент (1) в контейнере будут храниться 3 строчки, и это всех устраивает. QVector v; v.append("Морж1"); v.append("Морж2"); v.append("Морж3"); // (1) Дальше программист Иван замечает, что этот код можно упростить c помощью крутого синтаксиса: QVector v = QVector() << "Морж1" << "Морж2" << "Морж3"; ^ После этого приходит тимлид Илларион и видит, что в месте, отмеченном галочкой, выполняется ненужное копирование объекта (предположим, что Илларион не очень в курсе про 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"; Бабах! Иллариону приходит гневное письмо от системы Continuous Integration о том, что его коммит сломал несколько тестов. А ему еще повезло, что этот код был ими покрыт! А дело тут вот в чем (это реализация operator<< для QVector): inline QVector& operator<<(const T &t) { append(t); return *this; } Как можно заметить, последняя из операций (<<) возвращает ссылку (QVector&) на созданную чуть раньше временную переменную QVector. Естественно, что никто в таком случае не будет продлевать время жизни этой переменной - последняя операция << при синтаксическом разборе не имеет (и не нуждается в) информации о том, где и как была эта переменная была создана. Технически говоря, это rvalue, из которой путем неявного преобразования мы получили lvalue. Если подытожить, то const QVector& v - висячая ссылка, соответствующий ей объект уже уничтожен, а Илларион мог бы лишиться премии, если бы не писал тесты. Вот такая вот история.

Комментариев нет:

Отправить комментарий