Новый стандарт 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/
Комментарии
10 лет 32 недели назад
10 лет 33 недели назад
10 лет 33 недели назад
10 лет 33 недели назад
10 лет 35 недель назад
10 лет 35 недель назад
10 лет 35 недель назад
10 лет 45 недель назад
10 лет 45 недель назад
10 лет 46 недель назад