Новый стандарт C++ предлагает много улучшений и дополнений. Нововведения коснулись прежде всего ядра, а так же стандартной библиотеки STL. Есть и такие усовершенствования, при работе с которыми требуется оперировать совершенно новыми понятиями и заставлять себя мыслить иначе, мыслить в духе новых возможностей. Цель данной статьи рассмотреть некоторые из них.
Стоит отметить, что в то время как новый стандарт задействован в прикладном коде таких библиотек, как STL и Boost, то в Qt (на момент написания статьи только выпустили Qt 5.0) пока нет полноценной поддержки 11-х плюсов.
Здесь мы рассмотрим насколько полезными могут оказаться на практике rvalue-ссылки (предполагается, что читатель более-менее знаком с ними), и будет показано, как можно обойтись без них в определенных случаях при использовании QVector при отсутствии должной поддержки нового стандарта в Qt, получив при этом почти сходный результат.
Компилятор, который я использовал при написании и тестировании - GCC 4.7.2. Если вы используете Qt Cteator, не забудьте вставить строку CONFIG += c++11
в ваш .pro-файл. Буду благодарен тем, кто проделает данную работу с Clang и предоставит полученные результаты.
Итак, следующий класс продемонстрирует нам, что будет происходить с объектом при вставке/хранении его векторах:
#include <QVector> #include <QTextStream> #include <iostream> struct MoveTest { typedef MoveTest This; int i; // это поле не будет использоваться MoveTest () {} MoveTest (const This&) {std::cout << "constr copy\n";} MoveTest (This&&) noexcept {std::cout << "constr move\n";} ~MoveTest () noexcept {} This& operator= (const This&) {std::cout << "copy\n"; return *this;} This& operator= (This&&) noexcept {std::cout << "move\n"; return *this;} static void test() { std::cout << "std::move:\n"; This t1; This t2(std::move(t1)); This t3(std::move_if_noexcept(t2)); t2 = std::move(t3); t1 = std::move_if_noexcept(t2); std::cout << "\n"; std::cout << "QVector:\n"; QVector<This> qmTest(5); qmTest.insert(qmTest.begin(), t1); std::cout << "\n"; std::cout << "std::vector:\n"; std::vector<This> mTest(5); mTest.insert(mTest.begin(), t1); } }; int main(int argc, char *argv[]) { MoveTest::test(); }
Теперь давайте, запустим этот кусок на выполнение, потом выясним причинно-следственные связи:
std::move: constr move constr move move move QVector: constr copy constr copy constr copy constr copy constr copy constr copy copy copy copy copy copy copy std::vector: constr copy constr move constr move constr move constr move constr move
Как видно std::vector успешно перемещает 5 объектов типа MoveTest внутри себя. Это происходит после вставки объекта t1 (constr copy). В то же время QVector делает много лишних действий. Обратите внимание, как влияет спецификатор noexcept на вызов конструктора и оператора копирования. Если его убрать, то перемещение не будет работать внутри std::vector.
Для Qt, однако, не все так печально. Вызов конструктора копирования можно обойти использовав следующий макрос:
Q_DECLARE_TYPEINFO(MoveTest, Q_MOVABLE_TYPE);
Получим следующий вывод для QVector'а:
QVector: constr copy constr copy
Копирование происходит при вставке 2 раза, не зависимо от того, сколько элементов содержит QVector. Это достигается путем использования системной функции memmove. Но прежде чем использовать такой подход стоит учесть что не все объекты можно перемещать в памяти. Убедитесь, что внутри таких объектов не содержатся указатели на собственные поля. В этом случае будут возникать ошибки выполнения (сегфолты).
Если вы хотите сделать класс MoveTest шаблонным, то Q_DECLARE_TYPEINFO не сработает. Для шаблонов нужно использовать следующий макрос:
#define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \ template <typename T> class CONTAINER; \ template <typename T> \ class QTypeInfo< CONTAINER<T> > \ { \ public: \ enum { \ isPointer = false, \ isComplex = true, \ isStatic = false, \ isLarge = (sizeof(CONTAINER<T>) > sizeof(void*)), \ isDummy = false, \ sizeOf = sizeof(CONTAINER<T>) \ }; \ }; Q_DECLARE_MOVABLE_CONTAINER(MoveTest)
Этот макрос содержится в Qt, но по какой-то причине разработчики не предоставляют его рядовым программистам.
Замечание. Чтобы задействовать rvalue-ссылки, вы должны использовать std::move*. Для Qt справедливо было бы иметь что-то типа qMove. Глобально этот макрос не объявлен, но в коде Qt он все же имеется. Вы можете объявить его где-нибудь у себя в сторонке и использовать его втихаря.
Полезные ссылки:
http://www.rsdn.ru/article/submit/newcpp/newcpp.xml
http://ru.cppreference.com/w/cpp/language/noexcept
http://j.mp/cpp11ref
http://www.liveinternet.ru/users/3162595/post247699559/
Комментарии
9 лет 19 недель назад
9 лет 20 недель назад
9 лет 20 недель назад
9 лет 20 недель назад
9 лет 22 недели назад
9 лет 23 недели назад
9 лет 23 недели назад
9 лет 33 недели назад
9 лет 33 недели назад
9 лет 33 недели назад