C++14
C++14 – одна з попередніх версій стандарту ISO/IEC 14882 мови програмування C++ (повна назва: "International Standard ISO/IEC 14882:2014(E) Programming Language C++" ).[1]. C++14 можна розглядати як невелике розширення для C++11, яке в основному містить виправлення помилок і незначні покращення. Комітет з розробки нового стандарту опублікував чернетку N3690 15 травня 2013.[2]. Робочий варіант чернетки N3936 був опублікований 2 березня 2014 року, фінальний період голосування закритий 15 серпня 2014 року, а результат (одноголосне схвалення) оголошений 18 серпня 2014 року. Дата офіційного випуску C++14 – 15 грудня 2014.[3].
Версії мови програмування C++ |
---|
Оскільки, розробка стандарту була досить тривалою, і не було визначено року випуску, в період розробки також використовувалася назва “C++1y”, аналогічно до того, як стандарт C++11 до його випуску називали “C++0x”(випуск цієї версії очікували до 2010 року).
Описані нижче можливості мови відповідають чернетці N3797. Вони можуть дещо відрізнятися від остаточної версії стандарту.
Нові можливості мови
В цьому розділі наведені нові можливості мови в C++14.
Виведення типу повернення функцій
C++11 дозволяє виводити тип повернення значень для лямбда-функцій з типу повернення виразу. C++14 розширює цю можливість для всіх функцій. Новий стандарт також описує вивід типів для лямбда-функцій, які виглядають по-іншому, ніж return expression;
[4].
Для того, щоб використовувати автоматичний вивід типу повернення значення, функція має бути оголошення з типом повернення auto
, але без хвостового специфікатора типу повернення значення з C++11:
auto DeduceReturnType(); // Тип повернення значення буде визначений пізніше.
Якщо в тілі функції в різних місцях повертаються декілька виразів, то всі ці вирази повинні мати однаковий тип виведення.[5].
Функції, які використовують автовиведення типу повернення значення, можуть використовувати попереднє оголошення, але використовувати їх можна тільки після визначення. Ці визначення мають бути доступними в тій самій одиниці трансляції, в якій вони використовуються.
В таких функціях можна використовувати рекурсію, але рекурсивний виклик має виконуватися хоча б після одного повернення значення в цій функції[5]:
auto Correct(int i) {
if (i == 1)
return i; // тип повернення виводиться як int
return Correct(i-1)+i; // тепер можна викликати
}
auto Wrong(int i) {
if (i != 1)
return Wrong(i-1)+i; // Надто рано викликати рекурсію. Немає попереднього повернення
else
return i; // тип повернення виводиться як int
}
Альтернативний вивід типу при оголошенні
У C++11 були додані два способи виводу типів. auto
дозволяв створювати змінні з типом на основі виразу присвоєння. decltype
дозволяв визначити тип довільного виразу. Однак, типи, які виводилися decltype
і auto
, відрізнялися між собою. Зокрема, auto завжди виводить тип, який не є посиланням, так, якщо б використовувалось std::remove_reference
, в той час як auto&&
завжди виводить тип, який є посиланням. Тим не менше, результатом decltype
може бути як тип, який не є посиланням, так і тип, які є посиланням, залежно від виразу, який виводиться.[4]:
int i;
int&& f();
auto x3a = i; // decltype(x3a) - int
decltype(i) x3d = i; // decltype(x3d) - int
auto x4a = (i); // decltype(x4a) - int
decltype((i)) x4d = (i); // decltype(x4d) - int&
auto x5a = f(); // decltype(x5a) - int
decltype(f()) x5d = f(); // decltype(x5d) - int&&
В C++14 доданий синтаксис decltype(auto)
. Це дозволяє використовувати правила decltype
для оголошення auto
.
Синтаксис decltype(auto)
можна також використовувати для виводу типів повернення значень, якщо вказати decltype(auto)
замість auto
на місці типу повернення значення функції.
Зменшення обмежень на константні вирази
В C++11 представлена концепція constexpr-функцій: функції, які можуть виконуватися в час компіляції. Значення, які вони повертають, можуть використовуватися в операціях, де потрібний константний вираз, наприклад, як аргумент шаблону. Тим не менше, в C++11 constexpr-функції можуть містити тільки одне значення, яке буде повернуто(і також static_assert
і декілька інших оголошень).
В C++14 ці обмеження частково зняті. Constexpr-функції тепер можуть містити наступні елементи:[4]:
- Будь-які оголошення, крім:
static
абоthread_local
змінних.- Оголошення змінних без ініціалізаторів.
- Умовні оператори
if
іswitch
. - Всі оператори циклів, включаючи
for
. - Вирази, які змінюють значення об’єктів, якщо час життя цих об’єктів почався в constexpr-функції. До цього також відносяться виклики будь-яких не-
const
constexpr
нестатичних функцій-членів.
goto
вирази заборонені в constexpr-функціях C++14.
Крім того, в C++11 всі нестатичні методи, оголошені з constexpr
неявно вважалися const
–функціями по відношенню до this
. Це обмеження знято; нестатичні методи тепер можуть бути не const
[6]. Тим не менше, як зазначено раніше, не-const
constexpr
-метод може змінити поля класу тільки в тому випадку, коли час життя цього об’єкта почався під час аналізу константного виразу.
Шаблони змінних
В попередніх версіях C++ шаблонізація застосовувалася тільки для функцій і класів. C++14 дозволяє створювати шаблонні змінні. В запропонованому прикладі використовується шаблон змінної pi
, до якого можна звернутися, щоб отримати значення числа пі для різних типів (наприклад, 3
, при зчитуванні цілого типу; найбільш близьке значення до float
, double
або long double
при зчитуванні як float
, double
або long double
, відповідно, і т. д.).
template<typename T>
constexpr T pi = T(3.1415926535897932385);
// Usual specialization rules apply:
template<>
constexpr const char* pi<const char*> = "pi";
Агрегатна ініціалізація класів з ініціалізаторами полів
В C++11 були додані ініціалізатори полів класів — вирази, які застосовуються до полів на рівні класу, якщо конструктор їх не ініціалізує самостійно. Оголошення агрегатів було змінено, щоб ясно виключити всі класи з ініціалізаторами членів, тому для них агрегатна ініціалізація була неможливою.
C++14 знімає це обмеження[4] і розширює агрегатну ініціалізацію класів з ініціалізаторами полів. Якщо список ініціалізаторів в фігурних дужках не надає значення для цього аргументу, то цю роботу виконає ініціалізатор поля[7].
Літерали двійкових чисел
Числові літерали в C++14 можна задати у двійковій формі[4]. Синтаксис використовує префікси 0b
або 0B
. Схожий синтаксис також використовуєтьсяі в інших мовах, таки як Java, Python, Ruby.
Розділювачі розрядів
В C++14 можна використовувати апостроф для довільного розділення розрядів в числових літералах[8]. В деяких випадках це спрощує сприйняття більшості числових констант в коді і покращує читабельність коду.
auto integer_literal = 1'000'000;
auto floating_point_literal = 0.000'015'3;
auto binary_literal = 0b0100'1100'0110;
auto silly_example = 1'0'0'000'00;
Узагальнені лямбда-функції
В C++11 параметри [Анонімна функція|лямбда-функцій] потрібно було оголошувати вказуючи конкретні типи. C++14 знімає це обмеження і дозволяє оголошувати параметри лямбда-функцій зі специфікатором типу auto
[9].
auto lambda = [](auto x, auto y) {return x + y;};
Виведення типів параметрів узагальнених лямбда-функцій виконується згідно з правилами, подібними до виведення типів для auto
-змінних (але не є ідентичними).
Код, що розташований вище, еквівалентний тому, що нижче[10]:
struct unnamed_lambda
{
template<typename T, typename U>
auto operator()(T x, U y) const {return x + y;}
};
auto lambda = unnamed_lambda{};
Охоплення виразів для лямбда-функцій
Лямбда-функції C++11 дозволяють охоплювати змінні, оголошені у внутрішній області видимості, шляхом передачі за посиланням або за значенням. Це означає, що не можна охопити за значенням змінні типів, які допускають тільки змінні(але не допускають копіювання)[11]. C++14 дозволяє охоплювати змінні з ініціалізацією довільним виразом. Завдяки цьому, можна охоплювати змінні з переміщенням значення і оголошувати змінні з іменами, не оголошеними в більш високих областях видимості[9].
Охоплення виразів здійснюється за допомогою ініціалізаторів:
auto lambda = [value = 1] {return value;};
Лямбда-функція lambda
поверне 1, так як параметр value
був ініціалізований. Тип охопленого параметра виводиться з типу ініціалізатора, побідно до оглошення змінної зі специфікатором auto
.
Цю можливість можна використовувати для охоплення з переміщенням за допомогою стандартної функції std::move
:
std::unique_ptr<int> ptr(new int(10));
auto lambda = [value = std::move(ptr)] {return *value;};
Атрибут [[deprecated]]
[[deprecated]]
Атрибут deprecated
дозволяє маркувати сутності як застарілі. Звернення до них буде дозволено, але при компіляції буде виводитися попередження. Аргументом deprecated
може бути стрічковий літерал, який пояснює причину за старіння і/або можливу заміну.
[[deprecated]] int f();
[[deprecated("g() є потоко-небезпечним. Використовуйте h() натомість")]]
void g( int& x );
void h( int& x );
void test() {
int a = f(); // warning: 'f' is deprecated
g(a); // warning: 'g' is deprecated: g() є потоко-небезпечним. Використовуйте h() натомість
}
Нові функції стандартної бібліотеки
Загальні мьютекси і блокування
C++14 додає a загальні(shared) мьютекси і новий тип блокування для таких мьютексів.[12][13].
Гетерогенний пошук в асоціативних контейнерах
Стандартна бібліотека C++ визначає чотири асоціативні класи-контейнери. Ці класи дозволяють користувачу шукати значення на основі значення відповідного типу. Контейнери map дозволяють користувачу вказати ключ і значення, при цьому пошук відбувається по ключу і повертається значення. Тим не менше, пошук завжди виконується за конкретним типом ключа(для set ключем є саме значення).
C++14 дозволяє індексувати асоціативні контейнери значенням довільного типу, за умови, що існує перевантажений оператор порівняння, який може порівнювати значення цього типу зі значенням типу ключа контейнера[14]. Це дозволяє індексувати map-контейнери з типом ключа std::string
виразами типу const char*
, використовуючи перевантажений оператор порівняння operator<
.
Для збереження зворотної сумісності, гетерогенний пошук дозволений тільки тоді, коли компаратор, переданий асоціативному контейнеру, підтримує такий пошук. Класи стандартної бібліотеки std::less
(за замовчуванням для set- і map-контейнерів) і std::greater дозволяють здійснювати гетерогенний пошук[15].
Стандартні користувацькі літерали
В C++11 визначений синтаксис визначених користувачем літеральних суфіксів, але жоден з них не використовується в стандартній бібліотеці. C++14 додає наступні стандартні літерали[14]:
- "s", для створення різних std::basic_string типів.
- "h", "min", "s", "ms", "us", "ns", для створення відповідних часових інтервалів
std::chrono::duration
.
auto str = "hello world"s;
auto dur = 60s;
Два літерали "s" не впливають один на одного, оскільки стрічковий літерал працює виключно зі стрічками, а секундний діє тільки на числа.[16].
Адресація до кортежів по типу
std::tuple
, введені в C++11 дозволяє агрегувати декілька типізованих значен, які будуть проіндексовані в час компіляції. C++14 розширює функціонал кортежів для можливості звернення до елементів кортежу не тільки за індексом, але й за типом[14]. Якщо кортеж містить більше одного елемента типу, який вимагається, пошук призведе до помилки під час компіляції[17]:
tuple<string, string, int> t("foo", "bar", 7);
int i = get<int>(t); // i == 7
int j = get<2>(t); // Те саме, що і раніше: j == 7
string s = get<string>(t); // Помилка часу компіляції через неоднозначність
Інші зміни стандартної бібліотеки
std::make_unique
можна використовувати так як std::make_shared
для std::unique_ptr
[9]. об’єктів.
Для std::integral_constant
додано перевантажений оператор operator()
, який повертає константне значення.
За аналогією з глобальними функціями std::begin/std::end
додані std::cbegin/std::cend
, які повертають константні ітератори на початок і кінець діапазону.
Примітки
- ISO/IEC 14882:2014 — Information technology — Programming languages — C++. ISO. 14 січня 2014.
- Committee Draft, Standard for Programming Language C++ (PDF). ISO. 15 мая 2013.
- Sutter, Herb (18 серпня 2014). We have C++14!. Процитовано 18 серпня 2014.
- Wong, Michael (30 квітня 2013). The View from the C++ Standard meeting April 2013 Part 3. C/C++ Cafe. Процитовано 14 червня 2013.
- Merrill, Jason (17 апреля 2013). N3638 Return type deduction for normal functions (Revision 5). Процитовано 14 червня 2013.
- Smith, Richard (18 квітня 2013). N3652 Relaxing constraints on constexpr functions.
- Vandevoorde, Daveed; Voutilainen, Ville (17 квітня 2013). N3653 Member initializers and aggregates.
- Crowl, Lawrence; Smith, Richard; Snyder, Jeff; Vandevoorde, Daveed (25 вересня 2013). N3781 Single-Quotation-Mark as a Digit Separator.
- Саттер, Герб (20 квітня2013). Trip Report: ISO C++ Spring 2013 Meeting. isocpp.org. Процитовано 14 июня 2013.
- Faisal, Vali; Sutter, Herb; Abrahams, Dave (19 квітня2013). N3649 Generic (Polymorphic) Lambda Expressions (Revision 3).
- Move capture in Lambda. Stack Overflow.
- Wong, Michael (30 квітня 2013). The View from the C++ Standard meeting April 2013 Part 3. C/C++ Cafe. Процитовано 14 червня 2013.
- Howard, Hinnant; Vollmann, Detlef; Boehm, Hans (19 квітня 2013). N3659 Shared locking in C++ (Revision 2).
- Wong, Michael (26 квітня 2013). The View from the C++ Standard meeting April 2013 Part 2. C/C++ Cafe. Процитовано 14 червня 2013.
- N3657 Adding heterogeneous comparison lookup to associative containers (rev 4). 19 березня 2013.
- Peter, Sommerlad (18 квітня 2013). N3642 User-defined Literals for Standard Library Types (part 1 - version 4) (PDF).
- Spertus, Mike (19 апреля 2013). N3670 Wording for Addressing Tuples by Type: Revision 2.