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. Вони можуть дещо відрізнятися від остаточної версії стандарту.

Нові можливості мови

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

Посилання

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.