C++ 14

C++14 —վերջին ստանդարտի ոչ պաշտոնական անվանումը C++ ISO/IEC JTC1 է (ամբողջական անվանումը՝ «International Standard ISO/IEC 14882:2014(E) Programming Language C++». C++14 ը կարելի է դիտարկել որպես C++11-ի փոքրիկ ընդլայնում, որը հիմնականում պարունակում է սխալների ուղղում և որոշակի բարելավում։ Նոր ստանդարտը մշակող հանձնաժողովը N3690 սևագիրը թողարկեց 2013 թվականի մայիսի 15-ին։ N3936 սևագրի աշխատող տարբերակը թողարկվեց 2014 թվականի մարտի 2-ին, քվեարկության վերջնական փուլը ավարտվեց 2014 թվականի օգոստոսի 15-ին, իսկ արդյունքները ( միաձայն հավանությամբ ) հայտարարվեցին 2014 թվականի օգոստոսի 18 ին։

Քանի որ ստանդարտի մշակումը երկարատև էր, իսկ վերջնական տարբերակի հրապարակման թիվը հստակ չէր, մշակման ընթացքում օգտագործվում էր «C++1y» անունը, ինչպես C++11-ը, մինչ թողարկումը անվանում էին «C++0x» ( այս տարբերակի թողարկումը սպասվում էր մինչ 2010 թվականը)։

Ներքևում նկարագրվող լեզվի հնարավորությունները համապատասխանում են N3797 աշխատող սևագրին։ Հնարավոր է ստանդարտի վերջնական տարբերակից մի փոքր տարբերվեն։

Լեզվում կատարված փոփոխություններ 
Այս բաժնում ներկայացված են C++14 լեզվի միջուկի նոր հնարավորությունները։

Վերադարձվող արժեքի տիպի դուրսբերումը ֆունկցիայի համար 
C++11 թույլ է տալիս դուրս բերել լյաբդա-ֆունկցիաների վերադարձվող արժեքի տիպը, վերադարձվող արտահայտության տիպից։ C++14-ում այդ հնարավորությունը ընդգրկում է բոլոր ֆունկցիաները։ Նոր ստանդարտը լյաբդա-ֆունկցիաների համար նաև նկարագրում է տիպերի դուրսբերումը return expression-ից տարբերվող դեպքերում։

Վերադարձվող արժեքի տիպի ավտոմատ դուրս բերման համար ֆունկցիայի վերադարձվող արժեքի տիպը պետք է հայտարարված լինի auto, բայց ի տարբերություն նախորդ ստանդարտի ֆունկցիայի հայտարարման վերջում վերադարձվող տիպը բացահայտ նշելու կարիք չկա։
auto DeduceReturnType(); // տիպի վերադարձվող արժեքը հայտնի կլինի ավելի ուշ

Եթե ֆունկցիայի մարմնի տարբեր մասերից վերդարձնում է մի քանի արտահայտություններ, ապա այդ արտահայտություններից վերադարձվող տիպը պետք է նույնը լինի բոլոր արտահայտությունների համար։ Ֆունկցիաները՝ որոնք ավտոմատ կերպով են վերադարձնում տիպը, հնարավոր է օգտագործել նախնական հայտարարումը, սակայն հնարավոր է օգտագործել սահմանելուց հետո։ Այս սահմանումների թարգմանությունը պետք է հասանելի լինեն նույն տեղում որտեղ, որ օգտագործում են։
auto Correct(int i)
{
 if (i == 1)
 return i; // Ինչպես օրինակում վերադարձվող տիպը int է
 else
 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-ը հնարավորություն է տալիս տիպը որոշել կամայական արտահայտությունից։ Մեծ մասամբ auto-ն վերադարձնում է ոչ հղումային տիպ, եթե մշակված լիներ որպես std::remove_reference, ապա կվերադարձներ հղումային տիպի auto&&։ Այդուհանդերձ decltype-ի տիպը կարող է լինել ինչպես հղումային այնպես էլ ոչ հղումային կախված արտահայտությունից։
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-ն որտեղ ֆունկցիա արժեքը վերադարձնում է տիպը։
int i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(auto) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype(auto) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(auto) x5d = f(); // decltype(x5d) is int&&
auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i; // decltype(x7a) is int*
decltype(auto)*x7d = &i; // error, declared type is not plain

Հաստատուն արտահայտությունների սահմանափակումների նվազեցում 

C++11-ում ներկայացված է constexpr ֆունկցիա հասկացությունը։ Այս հասկացությունը հնարավորություն է տալիս ֆունկցիան իրականացնել կոմպիլացիա ժամանակ։ Վերադարձված արժեքները կարող են օգտագործվել այնպիսի արտահայտություններում որտեղ արտահայտությունը հաստատուն է, օրինակ. որպես կաղապարի փոփոխական։ Այնուհանդերձ constexpr ֆունկցիան C++11-ում կարող է վերադարձնել միայն մեկ արժեք (ինչպես նաև static_assert-ը և մի քանի այլ ֆունկցիաներ )։
C++ 14 –ում այդ երևույթների մի մասը հանած է։ Հիմա constexpr ֆունկցիան կարող է ունենալ հետևյալ տարրերը։
 Ցանկացած հայտարարում բացի
 static կամ thread_local փոփոխականներ
 հայտարարել փոփոխակաները առանց սկզբնավորելու
 Պայմանական արտահայտությունների ճյուղավորումը if և switch
 Բոլոր ցիկլիկ օպերատորների այդ թվում for-ի միջակայքերի համար
 Արտահայտությունները, որոնք փոխել են օբյեկտի արժեքը և եթե այդ օբյեկտները ստեղծվել են constexpr հրամանով, ապա սա ներառում է կանչեր ցանկացած ոչ constconstexpr, ոչ ստատիկ ֆունկցիայի անդամների համար։

goto-արտահայտությունում թույլ չի տալիս constexpr ֆունկցիաները C++14:

Սահմանափակումները ոչ constexpr ֆունկցիաների համար մնում է ուժի մեջ։ Այսպիսով եթե օգտագործում ենք for հրամանը միջակայքերի համար, ապա begin և end ֆունկցիաները կոնտեյնեռների համար պետք է լինեն գերբեռնված որպես constexpr։ Որպես կառուցողաղկան տիպ std::initializer_list ֆունկցիաները begin/end սահմանված են որպես constexpr ինչպես լոկալ, այնպես էլ ընդհանուր դեպքում։

Բացի այդ C ++ 11-ի բոլոր ոչ ստատիկ մեթոդները հայտարարել են ինչպես constexpr, վերջերս համարում էին const-ֆունկցիան դիմում է this-ին, բայց հիմա այդ սահմանափակումը հանած են, ոչ ստատիկ մեթոդները կարող են լինել ոչ const։ Այնուհանդերձ, ինչպես առաջ էր նշվում ոչ const constexpr մեթոդը կարող է փոխել դասսը այն դեպքում երբ այդ արտահայտությունը ստեղծվել է հաստատուն արտահայտության ստեղծման ժամանակ։

Փոփոխականների կաղապարներ 
C++-ի նախորդ տարբերակներում կաղապարները սահմանված էր միայն ֆունկցիաների և դասերի համար։ C++14-ը թույլ է տալիս ստեղծել կաղապար փոփոխականներ։ Օրնակ պի թվի ներկայացումը կաղապար փոփոխականով, կաղապար փոփոխականին տալով պի-ն ստանում ես նրա արժեքը, ամբողջ տիպի համար այն կլինի 3 (ավելի ճշգրիտ արժեք ստանալու համար տիպը պետք է ընտրել float, double կամ long double և այլն )։
Այսպիսի հայտարարումները և սահմանումները պատկանում է կաղապարների կանոններին։
template<typname T>
 constexpr T pi = T(3.1415926535897932385);

template<typname T>
T circular_area(T r)
 {
 return pi * r * r;  
}

Դասերի համախառն սկզբնարժեքավորում դաշտերի սկզբնարժեքավորողներով 
C++ 11-ում ավելացվել են դասերի դաշտերի սկզբնարժեքավորողներ- արտահայտություններ, կիրառվում է դաշտերի վրա դասի մակարդակում, եթե կոնստրուկտորը ինքնուրույն չի սկզբնարժեքավորում։ Ագրեգատների ինիցիալիզացումը փոփոխվել է, որպեսզի բացահայտորեն բացառի անդամների ինցիալիզացիաները, որովհետև դրանց համար ագրեգատայինինիցիալիզացիան անհար է։
C++14-ը հանում է այդ սահմանափակումը և ընդլայնում է դասերի համախառն սկզբնարժեքավորումը դաշտերի սկզբնարժեքավորողներով։ Եթե ձևավոր փակագծերի մեջ եղած սկզբնարժեքավորողների ցուցակը տվյալ արգումենտի համար արժեք չի հատկացնում, ապա այդ գործը իր վրա է վերցնում դաշտի սկզբնարժեքավորողը։

Երկուական թվերի լիտերալներ 
Թվային լիտերալները C++14 ում կարելի է հայտարարել երկուական տեսքով։ Գրելաձևը օգտագործում է 0բ կամ 0Բ նախդիրները։ Նմանատիպ գրելաձև օգտագործվում է նաև Java, Python, Perl и D լեզուներում։

Կարգերի բաժանիչներ 
C++14 –ում կարելի է օգտագործել ապաթարց թվային լիտերալներում կարգերի առանձնացման համար։ Որոշ դեպքերում դա հեշտացնում է մեծ հաստատունների ընկալումը և հեշտացնում է կոդի ընթերցելիությունը։
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 հատուկ բառով։
auto lambda = [](auto x, auto y)
{
 return x + y;
};
Ընդհանրացված լյամբդա-ֆունկցիաների պարամետրերի տիպերի դուրսբերումը կատարվում է auto-փոփոխականների տիպերի դուրսբերման օրենքներով (բայց ամբողջովին նույնը չէ)։ Վերևում ներկայացված կոդը համարժեք է հետևյալին.
struct unnamed_lambda
{
 template auto operator()(T x, U y) const 
 {
  return x + y;
 } 
};
auto lambda = unnamed_lambda();

Լյամբդա-ֆունկցիաների արտահայտությունների ընդունումը 
Լյամբդա-ֆունկցիաները C++11-ում թույլատրում են ընդունել ներքին տեսանելիության տիրույթում հայտարարված փոփոխականները, հղման կամ արժեքի փոխանցման ճանապարհով։ Դա նշանակում է որ չի կարելի արժեքով ընդունել միայն տեղափոխում թույլատրող( բայց կրկնօրինակում չթույլատրող ) տիպի փոփոխականները։ C++14-ը թույլատրում է ընդունել ցանկացած արտահայտությամբ սկզբնարժեքավորված փոփոխականներ։ Դրա շնորհիվ կարելի է ընդունել փոփոխականներ արժեքի տեղափոխումով և հայտարարել ավելի բարձր տեսանելույան տիրույթում չհայտատրարված անունով փոփոխականներ։
Արտահայտությունների ընդունումը իրականավում է սկզբնարժեքավորողների օգնությամբ
auto lambda = [value = 1]
{
 return value;
};
lambda լյամբդա-ֆունկցիան կվերադարձնի 1, քանի որ value պարամետրի համար աշխատել է համապատասխան սկզբնարժեքավորողը։ Ընդունված պարամետրի տրպը դուրս է բերվում սկզբնարժեքավորողի տիպից auto հատուկ բառի հայտարարության համաձայն։

lambda լյամբդա-ֆունկցիան կվերադարձնի 1, քանի որ value պարամետրի համար աշխատել է համապատասխան սկզբնարժեքավորողը։ Ընդունված պարամետրի տրպը դուրս է բերվում սկզբնարժեքավորողի տիպից auto հատուկ բառի հայտարարության համաձայն։
std::unique_ptr ptr(new int(10));
auto lambda = [value = std::move(ptr)]
{
 return *value;
};

Ատրիբուտ deprecated 
deprecated ատրիբուտը հնարավորություն է տալիս որոշակի երևույթներ նշել որպես հնեցված։ Այդպիսի երևույթների օգտագործման ժամանակ, կոմպիլյացիայի ընթացքում դուրս է բերվում զգուշացում։ deprecated ատրիբուտի արգումենտ կարող է հանդիսանալ տողային լիտեռալ, որը կբացատրի հնեցման պատճառը և/կամ դրա հնարավոր ալտերնատիվը։
[[deprecated]] int f();

[[deprecated("g()ապահով չէ. օգտագործեք h(), g() փոխարեն" ) ] ]
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(), g() փոխարեն
}

Ստանդարտ գրադարանի նոր ֆունկցաիներ

Բաժանվող մյուտեքսներ և կանխարգելում 
C++14 ստանդարտում ավելացված են բաժանված (shared) մյուտեքսներ և նոր կանխարգելման եղանակ մյուտեքսների համար։

Չսահմանված որոնում կոնտեյներներում 
C++ ստանդարտում գրադարնում սահմանված են չորս տեսակի ասոցատիվ դաս-կոնտեյներ։ Այդ դասերը հնարավորություն են տալիս արժեքների փնտրում հիմնվելով այդ դասի տիպի վրա։map կոնտեյներում նշվում է բանալիի և արժեքի տիպերը, իսկ որոնումը տեղի է ունենում բանալիով և վերադարձնում է արժեքը։ Ամեն դեպքում որոնումը կատարվում է ըստ բանալու՝ լինի դա բանալին map-ում թե արժեքը set-ում։ C++14 ստանդարտը թույլ է տալիս ստեղծել կամայական տիպի ասոցատիվ կոնտեյներներ. պայմանով, որ դրանք ունենան գերբեռնված համեմատման օպերատոր, որի միջոցով հնարավոր լինի համեմատել տվյալ տիպի օբյեկներ։ Սա հնարավորություն է տալիս std::string բանալու տիպով map-ում կատարել որոնում const char* տիպի օբյեկտի օգնությամբ օգտագործելով գերբեռնված operator<։Չսահմանված որոնումը թույլ է տալիս միայն այն ժամանակ, երբ կոնտեյներին փոխանցված կոմպարատորը ինքը թույլ է տալիս նման որոնումը։ Ստանդարտ գրադանի std::less և std::greater թույլ են տալիս կատարել չսահմանված որոնում։

Ստանդարտ օգտագործման լիտերալներ 
C++11 ստանդարտում նկարագրված է օգտագործողի կողմից սահմանվող լիտեռալային նախաբառերի սահմանման գրելաձև, բայց դրանցից ոչ մեկ չի օգտագործվում ստանդարտ գրադարանում։ C++14 ավելացնում է հետևյալ ստանդարտ լիտեռալները.
 «s»` std::basic_string տարբեր տիպերի համար
 «h», «min», «s», «ms», «us», «ns» ՝ std::chrono::duration տիպի ժամանակային լիտեռալների համար
string str = "hello world"s;
chrono::duration dur = 60s;
Երկու «s» լիտեռալները չեն ազդում իրար վրա, քանի որ տողային լիտեռալը աշխատում է միայն տողային լիտեռալի հետ, ժամանակայինը միայն թվերի։

Խմբակների հասցեավորումը տիպերով 
std::tuple-ը, որը ավելացվել է C++11 ստանդարտում թույլ է տալիս միմյանց կցել մի քանի տարբեր տիպերի արժեքներ որոնք կինդեքսավորվեն կոմպիլյացիայի ժամանակ։C++14 ընդլայնում է խմբակների ֆունկցիոնալությունը թույլ տալով դրանց էլեմենտներին դիմել ոչ միայն ինդեքսով այլև տիպի միջոցով։ Եթե խմբակը պարումնակում է նույն տիպի մի քանի օբյեկտներ ապա տիպի միջոցով դրա անդամներին դիմելը կբերի կոմպիլյացիայի սխալի։
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 տիպի օբյեկտների համար։ std::integral_constant-ի համար ավելացվել է գերբեռնված operator(), որը կոնստանտ արժեք է վերադարձնում։ std::begin/std::end գլոբալ ֆունկցիաների անալոգիայով ավելացվել է std::cbegin/std::cend, որոնք վերադաձնում են կոնստանտ իտերատորներ դիապազոնի սկզբի և վերջի վրա։

Ծանոթագրություններ 

C++