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++1y”, аналогічно до того, як стандарт C++11 до його випуску називали “C++0x”(випуск цієї версії очікували до 2010 року).

Описані нижче можливості мови відповідають чернетці N3797 [Архівовано 11 січня 2020 у Wayback Machine.]. Вони можуть дещо відрізнятися від остаточної версії стандарту [Архівовано 29 січня 2020 у Wayback Machine.].

Нові можливості мови[ред. | ред. код]

В цьому розділі наведені нові можливості мови в 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]. Тим не менше, як зазначено раніше, не-constconstexpr-метод може змінити поля класу тільки в тому випадку, коли час життя цього об’єкта почався під час аналізу константного виразу.

Шаблони змінних[ред. | ред. код]

В попередніх версіях 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]] 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, які повертають константні ітератори на початок і кінець діапазону.

Примітки[ред. | ред. код]

  1. ISO/IEC 14882:2014 — Information technology — Programming languages — C++. ISO. 14 January 2014. Архів оригіналу за 29 січня 2017. Процитовано 2 червня 2015.
  2. Committee Draft, Standard for Programming Language C++ (PDF). ISO. 15 мая 2013. Архів оригіналу (PDF) за 21 січня 2022. Процитовано 2 червня 2015.
  3. Sutter, Herb (18 серпня 2014), We have C++14!, архів оригіналу за 19 серпня 2014, процитовано 18 серпня 2014
  4. а б в г д Wong, Michael (30 квітня 2013). The View from the C++ Standard meeting April 2013 Part 3. C/C++ Cafe. Архів оригіналу за 13 жовтня 2013. Процитовано 14 червня 2013.
  5. а б Merrill, Jason (17 апреля 2013). N3638 Return type deduction for normal functions (Revision 5). Архів оригіналу за 25 серпня 2013. Процитовано 14 червня 2013.
  6. Smith, Richard (18 квітня 2013). N3652 Relaxing constraints on constexpr functions. Архів оригіналу за 25 серпня 2013. Процитовано 2 червня 2015.
  7. Vandevoorde, Daveed; Voutilainen, Ville (17 квітня 2013). N3653 Member initializers and aggregates. Архів оригіналу за 25 серпня 2013. Процитовано 2 червня 2015.
  8. Crowl, Lawrence; Smith, Richard; Snyder, Jeff; Vandevoorde, Daveed (25 вересня 2013). N3781 Single-Quotation-Mark as a Digit Separator (PDF). Архів оригіналу (PDF) за 13 квітня 2014. Процитовано 2 червня 2015.
  9. а б в Саттер, Герб (20 квітня2013). Trip Report: ISO C++ Spring 2013 Meeting. isocpp.org. Архів оригіналу за 20 серпня 2017. Процитовано 14 июня 2013.
  10. Faisal, Vali; Sutter, Herb; Abrahams, Dave (19 квітня2013). N3649 Generic (Polymorphic) Lambda Expressions (Revision 3). Архів оригіналу за 25 серпня 2013. Процитовано 2 червня 2015.
  11. Move capture in Lambda. Stack Overflow. Архів оригіналу за 24 січня 2013. Процитовано 2 червня 2015.
  12. Wong, Michael (30 квітня 2013). The View from the C++ Standard meeting April 2013 Part 3. C/C++ Cafe. Архів оригіналу за 13 жовтня 2013. Процитовано 14 червня 2013.
  13. Howard, Hinnant; Vollmann, Detlef; Boehm, Hans (19 квітня 2013). N3659 Shared locking in C++ (Revision 2). Архів оригіналу за 19 серпня 2013. Процитовано 2 червня 2015.
  14. а б в Wong, Michael (26 квітня 2013). The View from the C++ Standard meeting April 2013 Part 2. C/C++ Cafe. Архів оригіналу за 13 жовтня 2013. Процитовано 14 червня 2013.
  15. N3657 Adding heterogeneous comparison lookup to associative containers (rev 4). 19 березня 2013. Архів оригіналу за 19 серпня 2013. Процитовано 2 червня 2015.
  16. Peter, Sommerlad (18 квітня 2013). N3642 User-defined Literals for Standard Library Types (part 1 - version 4) (PDF). Архів оригіналу (PDF) за 25 серпня 2013. Процитовано 2 червня 2015.
  17. Spertus, Mike (19 апреля 2013). N3670 Wording for Addressing Tuples by Type: Revision 2. Архів оригіналу за 19 серпня 2013. Процитовано 2 червня 2015.

Посилання[ред. | ред. код]