C++14 Qt програмістів

У цій статті описується, яким чином зміни, принесені стандартом С++14 вплинули або можуть вплинути на розробці додатків Qt. Дана стаття орієнтована не тільки на Qt програмістів, а також на всіх тих, кому цікаво розвиток С++. Автор оригіналу — Olivier Goffart, який є одним із розробників Qt moc (meta-object compiler).


Узагальнені лямбда-функції

В С++11 були введені лямбда-функції, і Qt5 дозволяє використовувати їх в сигналах. C++14 спрощує використання лямбда-функцій, так як тепер тип аргументів може бути виведений автоматично, тобто стало можливим використання auto тип параметра замість того, щоб описувати цей тип:
connect(sender, &Sender::valueChanged, [=](const auto &newValue) {
receiver->updateValue("senderValue", newValue);
});

Лямбда-функція являє собою функтор з реалізованим оператором operator(). В узагальнених лямбда-функціях цей оператор оголошений як шаблонна функція. Я зробив зміни, які підтримують такі функтори і ці зміни були включені в Qt 5.1. C++14 також додає можливість захоплення не тільки змінних, але і виразів:
connect(sender, &Sender::valueChanged, [reciever=getReciever()](const auto &newValue) {
receiver->updateValue("senderValue", newValue);
});


Пом'якшення вимог до константным виразами

В С++11 було введено нове ключове слово constexpr. В Qt 4.8 був введений новий макрос Q_DECL_CONSTEXPR, який розгортається в constexpr, якщо це слово підтримується компілятором. В Qt 5 цей макрос використовується для великої кількості функцій, де це представляється можливим.
В С++14 були пом'якшені вимоги, що пред'являються до константным виразами. З++11 дозволяв використовувати constexpr тільки з єдиним оператором повернення і тільки у функціях-членах з модифікатором const. З++14 дозволяє набагато більше, лише б обчислення могло відбуватися під час компіляції.
/*
Ця функція не відбудеться створення в С++11, тому що складається з кількох компонентів, містить цикл і внутрішню змінну.
Але у С++це дозволено 14
*/ 
constexpr int myFunction(int v) {
int x = 1;
while (x < v*v)
x*=2;
return x;
}

Функції-члени класу, оголошені як constexpr в С++11 автоматично трактуються як константные, тобто, не змінюють поля класу. В С++14 неконстантная функція-член класу теж може бути constexpr. Результатом такої зміни стало те, що функції-члени класів, оголошені constexpr, але не мають явно зазначеного модифікатора const, стали неконстантными в С++14, а це означає несумісність на рівні бінарних файлів. На щастя, в Qt макрос Q_DECL_CONSTEXPR явно оголошував всі функції-члени класів константними, тому ніякого порушення бінарної сумісності при його використанні немає.
Отже, тепер ми можемо обчислювати під час компіляції неконстантние функції класів, такі, наприклад, як operator=. Для цього в Qt 5.5 буде введено новий макрос Q_DECL_RELAXED_CONSTEXPR, який буде розгортатися в constexpr, якщо компілятор в режимі С++14.

Невеликі зміни в З++14

Стандарт С++14 привніс деяку кількість невеликих змін, мета яких — зробити розробку більш зручною. Ці зміни не мають безпосереднього впливу на Qt, але цілком можуть бути використані у Ваших програмах, якщо використовується компілятор з підтримкою С++14.

Роздільниками розрядів чисел
Якщо потрібно визначити велику константу в коді, можна використовувати апостроф в якості роздільника розрядів:
int i = 123'456'789;


Двійкові константи
В С++ можна визначати десяткові, вісімкових (починаються з 0), і шістнадцяткові (починаються з 0x) константи. Тепер з'явилася можливість визначати і двійкові константи, використовуючи префікс 0b:
int i = 0b0001'0000'0001;


Автоматичний висновок типу значення, що повертається
Якщо у Вас є вбудована (inline) функція, то Ви можете використовувати ключове слово auto як вказівки повертається типу, його тепер можна не вказувати явно. Компілятор сам його виведе:
// зворотний тип буде виведений як 'int'
auto sum(int a, int b) { return a+b; }

Це, на жаль, не підтримується Qt слотів або так званих invocable методів, так як Qt moc не в змозі сам визначити зворотний тип.

Шаблонні змінні
Раніше було можливим зробити шаблонну функцію або клас. Зараз можна зробити шаблонної і просто змінну.
template < typename T> const T pi = 3.141592653589793;
/*...*/
float f = pi<float>;
double d = pi<double>;


Ініціалізація структур
В С++11 стало можливо ініціалізувати структуру, у якої немає певного користувачем конструктора, списком ініціалізації значення полів у фігурних дужках), а також з'явилася можливість привласнювати нестатическим полів класу значення за замовчуванням прямо у визначенні класу. Але в З++11 не можна було використовувати відразу обидва ці варіанти ініціалізації. В С++14 тепер можна. Цей код буде працювати саме так, як і очікується:
struct MyStruct {
int x;
QString str;
bool flag = false;
QByteArray str2 = "something";
};
// ...
// не відбудеться створення в C++11 бо MyStruct не POD
MyStruct s = { 12, "1234", true };
Q_ASSERT(s.str2 == "something");


Квалификаторы посилань для методів класів

Це насправді було привнесено не в З++14, а ще в С++11, але ми почали використовувати ці квалификаторы тільки в Qt5, і я не згадував про них у попередніх постах, тому поговоримо про них зараз.
Розглянемо наступний код:
QString lower = QString::fromUtf8(data).toLower();

Тут fromUtf8 повертає тимчасову змінну. Було б непогано, якби метод toLower використовував вже виділену пам'ять для цієї тимчасової змінної і виконав в ній необхідні перетворення. Саме для таких випадків і були введені квалификаторы посилань для функцій-членів класів.
Спрощений код з qstring.h:
class QString {
public:
/* ... */
QString toLower() const &
{ /* ... повертає копію з усіма символами в нижньому регістрі ... */ }
QString toLower() &&
{ /* ... виконує перетворення вихідної рядку ... */ }
/* ... */
};

Зверніть увагу на '&' і '&&' в кінці методів toLower. Це квалификаторы посилань і дозволяють перевантажити функцію в залежності від типу, на який вказує 'this', таким же чином, як квалификатор const дозволяє перевантажений метод залежно від константності 'this'. У разі, коли toLower викликається для тимчасової змінної (rvalue) буде обраний другий метод (який з &&) і проведе зміни рядки, не копіюючи її.
Функції, які були покращені з допомогою цих кваліфікаторов в Qt 5.4: QString::toUpper, QString::toLower, QString::toCaseFolded, QString::toLatin1, QString::toLocal8Bit, QString::toUtf8, QByteArray::toUpper, QByteArray::toLower, QImage::convertToFormat, QImage::mirorred, QImage::rgbSwapped, QVersionNumber::normalized, QVersionNumber::segment

Зміни в стандартній бібліотеці

З++11 і С++14 додали багато конструкцій в стандартну бібліотеку, які багато в чому перегукуються з наявними конструкціями в QtCore. В Qt стандартна бібліотека використовується дуже мало. Ми взагалі не хочемо, щоб бібліотека була частиною ABI. Це дозволить залишатися бінарно сумісними навіть якщо стандартна бібліотека зміниться (наприклад libstdc++ і libcpp). Також Qt досі підтримує деякі старі платформи, на яких немає стандартної бібліотеки С++11. З цих причин ми обмежуємо використання цієї бібліотеки.
Але є виняток — Qt5 оголошує свої алгоритми застарілими (deprecated) і зараз рекомендується використовувати алгоритми STL (наприклад std::sort замість qSort).

Висновок

Звичайно, може пройти якийсь час, перш ніж Ви зможете використовувати нові конструкції З++14 в своїх проектах. Але я сподіваюся, що ви почнете їх застосовувати як і багато інші (Qt Creator, KDE, LLVM). У нових компіляторах MSVC C++14 активний за замовчуванням, в clang і gcc потрібно використовувати спеціальний прапор (на даний момент це-std=c++1y). З допомогою qmake можна налаштувати свій проект на збірку з С++14 починаючи з Qt5.4 використовуючи наступну команду: CONFIG += c++14

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.