{"id":29431,"date":"2024-11-13T05:00:00","date_gmt":"2024-11-13T04:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=29431"},"modified":"2024-11-13T09:03:14","modified_gmt":"2024-11-13T08:03:14","slug":"nowe-funkcjonalnosci-jezyka-w-c23","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/nowe-funkcjonalnosci-jezyka-w-c23\/","title":{"rendered":"Nowe funkcjonalno\u015bci j\u0119zyka w C++23"},"content":{"rendered":"\n<p>C++ jest j\u0119zykiem, kt\u00f3ry wci\u0105\u017c ewoluuje. Od C++11 co 3 lata publikowana jest nowa wersja standardu. Publikacje proponuj\u0105ce zmiany (<a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >aktualna lista online<\/a><a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/\" rel=\"nofollow\" >)<\/a> s\u0105 nieustannie wysy\u0142ane do Komitetu C++, kt\u00f3ry g\u0142osuje nad rozwi\u0105zaniami. Dotycz\u0105 one zar\u00f3wno samego j\u0119zyka jak i biblioteki standardowej. Prace nad C++23 zosta\u0142y zako\u0144czone, a Komitet zajmuje si\u0119 obecnie C++26.<\/p>\n\n\n\n<p>W artykule przedstawi\u0119 moim zdaniem najwa\u017cniejsze zmiany w j\u0119zyku w standardzie C++23.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">if consteval<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Powt\u00f3rka z if constexpr<\/h3>\n\n\n\n<p>Najpewniej wiesz ju\u017c, czym jest <strong>if constexpr<\/strong>. Pozwala warunkowo kompilowa\u0107 instrukcje (statements). Po nim wyst\u0119puje warunek, kt\u00f3rego warto\u015b\u0107 musi by\u0107 wyra\u017ceniem kontekstowo przekszta\u0142conym na <strong>bool<\/strong>, za\u015b konwersja musi by\u0107 wyra\u017ceniem sta\u0142ym (constant expression). <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate&lt;typename T&gt;\ndecltype(auto) CallFooOrBarMember(const T&amp; t)\n{\n         if constexpr (requires { t.Foo(); }) { return t.Foo(); }\n    else if constexpr (requires { t.Bar(); }) { return t.Bar(); }\n}\n\nstruct Caller0\n{\n    int Foo() const { return 42; }\n    char Bar() const { return &#039;E&#039;; }\n};\n\nstruct Caller1\n{\n    int Foo() { return 7; }\n    static double Bar() { return 3.14; }\n};\n<\/pre><\/div>\n\n\n<p>W powy\u017cszym przyk\u0142adzie:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>typem zwracanym przez <strong>CallFooOrBarMember(Caller0{})<\/strong> jest<strong> int<\/strong>. <strong>CallFooOrBarMember<\/strong> wo\u0142a <strong>Caller0::Foo<\/strong>, poniewa\u017c pierwszym spe\u0142nionym warunkiem jest to, \u017ce wyra\u017cenie <strong>t.Foo();<\/strong> jest prawid\u0142owe dla <strong>const Caller0&amp; t (t.Bar();<\/strong> jest r\u00f3wnie\u017c prawid\u0142owe dla <strong>const Caller0&amp; t<\/strong>, ale przez spe\u0142nienie pierwszego warunku, ga\u0142\u0105\u017a <strong>if constexpr<\/strong> zostaje pomini\u0119ta.<\/li>\n\n\n\n<li>typem zwracanym przez <strong>CallFooOrBarMember(Caller1{})<\/strong> jest <strong>double<\/strong>. <strong>CallFooOrBarMember<\/strong> wo\u0142a <strong>Caller1::Bar<\/strong>, poniewa\u017c <strong>t.Foo();<\/strong> jest prawid\u0142owe dla<strong> Caller1&amp; t,<\/strong> ale nie dla <strong>const Caller1&amp; t,<\/strong> co zostawia prawid\u0142owo\u015b\u0107 wyra\u017cenia <strong>t.Bar();<\/strong> jako pierwszy spe\u0142niony warunek.<\/li>\n\n\n\n<li>typem zwracanym przez <strong>CallFooOrBarMember(int{})<\/strong> jest <strong>void<\/strong>, poniewa\u017c <strong>int<\/strong> nie spe\u0142nia \u017cadnego warunku, co zostawia puste cia\u0142o funkcji.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Czas wykonywania (run time) a czas kompilacji (compile time)<\/h2>\n\n\n\n<p>Wersja C++20 dostarczy\u0142a funkcj\u0119 do rozpoznania, czy jej wywo\u0142anie odbywa si\u0119 w kontek\u015bcie okre\u015blania sta\u0142ej warto\u015bci (constant-evaluated context, warto\u015b\u0107 mo\u017ce by\u0107 okre\u015blona w czasie kompilacji). By\u0142a to funkcja <strong>is_constant_evaluated().<\/strong><\/p>\n\n\n\n<p>Oto przyk\u0142ad u\u017cycia (zak\u0142adaj\u0105c, \u017ce <strong>std::strlen<\/strong> nie mo\u017ce by\u0107 wo\u0142ane w kontek\u015bcie okre\u015blania sta\u0142ej warto\u015bci):<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nconstexpr size_t stringLength(const char* str)\n{\n    if (std::is_constant_evaluated())\n    {\n        size_t size = 0;\n        while (*str != &#039;\\0&#039;) { ++size; ++str; }\n        return size;\n    }\n    else\n    {\n        return std::strlen(str);\n    }\n}\n<\/pre><\/div>\n\n\n<p>Jak wida\u0107, jest to zwyczajna instrukcja<strong> if<\/strong>, a nie <strong>if constexpr<\/strong>. Powodem tego jest zachowanie funkcji <strong>is_constant_evaluated<\/strong>. Zwraca ona <strong>true<\/strong>, gdy jest w kontek\u015bcie okre\u015blania sta\u0142ej warto\u015bci, a wstawianie jej wywo\u0142ania w <strong>if constexpr<\/strong> tworzy taki kontekst, co spowodowa\u0142oby odrzucenie ga\u0142\u0119zi false (nie jest w og\u00f3le kompilowana), kt\u00f3ra jest dla kontekstu run time.<\/p>\n\n\n\n<p>Taki spos\u00f3b u\u017cycia tej funkcji jest podatny na b\u0142\u0119dy, dlatego C++23 dostarczy\u0142: <strong>if consteval<\/strong>. Instrukcja nie wykorzystuje warunku, poniewa\u017c warunek okre\u015blania sta\u0142ej warto\u015bci (constant evaluation, czasu kompilacji) jest w jej nazwie. Zastosowanie tej instrukcji jest takie samo, jak wspomnianej funkcji, ale teraz jest to bardziej elegancka funkcjonalno\u015b\u0107 samego j\u0119zyka bez wykorzystania \u017cadnej biblioteki.<\/p>\n\n\n\n<p>Ostatni przyk\u0142ad mo\u017cna zmieni\u0107 na:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nconstexpr size_t stringLength(const char* str)\n{\n    if consteval\n    {\n        size_t size = 0;\n        while (*str != &#039;\\0&#039;) { ++size; ++str; }\n        return size;\n    }\n    else\n    {\n        return std::strlen(str);\n    }\n}\n<\/pre><\/div>\n\n\n<p>Nale\u017cy wspomnie\u0107, \u017ce instrukcja ga\u0142\u0119zi true (oraz instrukcja ga\u0142\u0119zi false, gdy jest obecna) musi by\u0107 instrukcj\u0105 z\u0142o\u017con\u0105 (compound statement).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nif consteval doThis(); else doThat(); \/\/ b\u0142\u0105d\nif consteval { doThis(); } else { doThat(); } \/\/ prawid\u0142owo\n<\/pre><\/div>\n\n\n<p>Mo\u017cna te\u017c u\u017cy\u0107 alternatywnej instrukcji:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nif !consteval { nonConstEvalContext(); } else { constEvalContext(); }\n<\/pre><\/div>\n\n\n<p>kt\u00f3ra odpowiada:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nif consteval { constEvalContext(); } else { nonConstEvalContext(); }\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Jawny parametr obiektu (explicit object parameter)<\/h2>\n\n\n\n<p>Istnieje nowy spos\u00f3b, by zadeklarowa\u0107 niestatyczn\u0105 funkcj\u0119 sk\u0142adow\u0105 (non-static member function): jako pierwszy parametr funkcja przyjmuje jawny parametr obiektu poprzedzony s\u0142owem kluczowym <strong>this<\/strong>. Na potrzeby tego artyku\u0142u nazwijmy to <strong>FEOP<\/strong> (function with explicit object parameter \u2013 funkcja z jawnym parametrem obiektu).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstruct OldAppleTree\n{\n    int appleCount;\n\n    void pickApple(int count) &amp;;\n    int getCombinedAppleCount(const AppleTree&amp; other) const &amp;;\n};\n\nstruct AppleTree\n{\n    int appleCount;\n\n    \/\/ odpowiedniki funkcji struktury OldAppleTree:\n    void pickApple(this AppleTree&amp; self, int count);\n    int getCombinedAppleCount(this const AppleTree&amp; self, const AppleTree&amp; other);\n\n    \/\/ bez odpowiadaj\u0105cej wersji; przekazywanie obiektu przez warto\u015b\u0107 (pass by value, kopiuje *this)\n    int getTripledAppleCount(this AppleTree self);\n};\n<\/pre><\/div>\n\n\n<p>Wydaje si\u0119 bardziej rozwlek\u0142e ni\u017c stary spos\u00f3b. Zanim ujawni\u0119 jego potencja\u0142 do upraszczania rzeczy, chcia\u0142bym wspomnie\u0107 o paru r\u00f3\u017cnicach:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>wska\u017anik <strong>this<\/strong> nie mo\u017ce by\u0107 u\u017cyty w ciele FEOP<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid AppleTree::pickApple(this AppleTree&amp; self, int count)\n{\n    \/\/ appleCount -= count;       \/\/ b\u0142\u0105d\n    \/\/ this-&gt;appleCount -= count; \/\/ b\u0142\u0105d\n    self.appleCount -= count;\n}\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>wska\u017anik na FEOP jest wska\u017anikiem na funkcj\u0119, a nie wska\u017anikiem na sk\u0142adow\u0105 klasy (member)<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nOldAppleTree oldTree{6};\nauto oldFun = &amp;OldAppleTree::pickApple;\n\/\/ typem oldFun jest: void (OldAppleTree::*)(int) &amp;&#039;\n(oldTree.*oldFun)(2);\n\nAppleTree newTree{7};\nauto newFun = &amp;AppleTree::pickApple;\n\/\/ typem newFun jest: void (*)(AppleTree&amp;, int)&#039;\nnewFun(newTree, 2);\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>FEOP musi by\u0107 niestatyczna, niewirtualna oraz bez kwalifikator\u00f3w cv (cv-qualifiers: const, volatile) i kwalifikator\u00f3w ref (ref-qualifiers: &amp;, &amp;&amp;) (te kwalifikatory, o ile obecne, powinny by\u0107 stosowane przy jawnym parametrze obiektu np. v<strong>oid fun(this const<\/strong> <strong>X&amp; self)<\/strong>).<\/li>\n<\/ul>\n\n\n\n<p>U\u017cywanie jawnego parametru obiektu w szablonach funkcji sk\u0142adowych (member function templates) pozwala na dedukcj\u0119 typu i kategorii warto\u015bci (value category) wywo\u0142uj\u0105cego obiektu. Ta funkcjonalno\u015b\u0107 to &#8222;<strong>deducing this<\/strong>&#8222;. Pozwala ona na zredukowanie kodu, je\u015bli chodzi o kwalifikatory cv i ref:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nclass OldVec\n{\nprivate:\n    Apple* array;\npublic:\n          Apple&amp;  operator&#x5B;](size_t id)       &amp;  { return array&#x5B;id]; }\n    const Apple&amp;  operator&#x5B;](size_t id) const &amp;  { return array&#x5B;id]; }\n          Apple&amp;&amp; operator&#x5B;](size_t id)       &amp;&amp; { return std::move(array&#x5B;id]); }\n    const Apple&amp;&amp; operator&#x5B;](size_t id) const &amp;&amp; { return std::move(array&#x5B;id]); }\n};\n\nclass NewVec\n{\nprivate:\n    Apple* array;\npublic:\n    \/\/ skr\u00f3cony szablon funkcji (abbreviated function template) przy u\u017cyciu parametru typu auto\n    auto&amp;&amp; operator&#x5B;](this auto&amp;&amp; self, size_t id)\n    {\n        \/\/ forward_like propaguje kategori\u0119 warto\u015bci (value category)\n        \/\/ oraz kwalifikacj\u0119 const (const-qualification)\n        return std::forward_like&lt;decltype(self)&gt;(self.array&#x5B;id]);\n    }\n};\n<\/pre><\/div>\n\n\n<p><strong>Oto jest DRY w akcji<\/strong>.<\/p>\n\n\n\n<p>Co to <strong>std::forward_like<\/strong>? W skr\u00f3cie \u2013 pozwala propagowa\u0107 kategori\u0119 warto\u015bci i kwalifikacj\u0119 const. Dzia\u0142a podobnie do <strong>std::forward<\/strong>, z tym \u017ce pierwszym parametrem szablonu mo\u017ce by\u0107 typ zupe\u0142nie inny od typu parametru funkcji i zwraca referencj\u0119 analogiczn\u0105 do pierwszego parametru szablonu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nApple apple;\nconst Apple capple;\n\nstd::forward_like&lt;const NewVec&amp;&amp;&gt;(std::move(apple)); \/\/ zwraca const Apple&amp;&amp;\nstd::forward_like&lt;const NewVec&amp;&amp;&gt;(apple);            \/\/ zwraca const Apple&amp;&amp;\nstd::forward_like&lt;NewVec&amp;&gt;(std::move(apple));        \/\/ zwraca Apple&amp;\nstd::forward_like&lt;NewVec&amp;&gt;(apple);                   \/\/ zwraca Apple&amp;\nstd::forward_like&lt;NewVec&amp;&gt;(std::move(capple));       \/\/ zwraca const Apple&amp;\nstd::forward_like&lt;NewVec&amp;&gt;(capple);                  \/\/ zwraca const Apple&amp;\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">CRTP<\/h3>\n\n\n\n<p>Curiously Recurring Template Pattern (tzw. ciekawie powtarzaj\u0105cy si\u0119 wz\u00f3r szablonu) mo\u017cna go upro\u015bci\u0107, u\u017cywaj\u0105c dedukuj\u0105cego <strong>this<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>po staremu: klasa bazowa musi by\u0107 szablonowa<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate&lt;typename Derived&gt;\nstruct Base\n{\n    void process()\n    {\n        commonCalculations();\n        static_cast&lt;Derived*&gt;(this)-&gt;specializedPart();\n        commonCalculations2();\n    }\n};\n\nstruct Deriv : public Base&lt;Deriv&gt; { specializedPart() {} };\n\nDeriv d;\nd.process();\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>po nowemu: funkcje sk\u0142adowe (member functions) s\u0105 szablonowe, a klasa dziedzicz\u0105ca jest wydedukowana<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstruct Base\n{\n    void process(this auto&amp;&amp; self)\n    {\n        commonCalculations();\n        self.specializedPart();\n        commonCalculations2();\n    }\n};\n\nstruct Deriv : public Base { specializedPart() {} };\n\nDeriv d;\nd.process();\n<\/pre><\/div>\n\n\n<p>Jak wida\u0107, jest to mniej podatne na b\u0142\u0119dy, poniewa\u017c nie u\u017cywa si\u0119 jawnie nazwy klasy dziedzicz\u0105cej.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wielowymiarowy operator indeksu (multidimensional subscript operator)<\/h2>\n\n\n\n<p>C++23 pozwala na przeci\u0105\u017cenie operatora indeksu (subscript operator, <strong>operator[]<\/strong>) dla wi\u0119cej ni\u017c jednego parametru:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstruct Apple { int seedCount = 0; };\n\nclass The5DArray\n{\nprivate:\n    Apple* appleArray;\n    std::array&lt;size_t, 5&gt; dim;\npublic:\n    The5DArray(Apple* appleArray, const std::array&lt;size_t, 5&gt;&amp; dim)\n        : appleArray{appleArray}, dim{dim} {}\n        \n    Apple&amp; operator&#x5B;](size_t id0, size_t id1, size_t id2, size_t id3, size_t id4)\n    {\n        return appleArray&#x5B;id0 * dim&#x5B;1] * dim&#x5B;2] * dim&#x5B;3] * dim&#x5B;4]\n                        + id1 * dim&#x5B;2] * dim&#x5B;3] * dim&#x5B;4]\n                        + id2 * dim&#x5B;3] * dim&#x5B;4]\n                        + id3 * dim&#x5B;4]\n                        + id4];\n    }\n};\n\nint main()\n{\n    constexpr std::array&lt;size_t, 5&gt; dimensions{ 3, 3, 3, 3, 3 };\n    constexpr size_t dimProduct = std::ranges::fold_left(dimensions, 1,\n                                                         std::multiplies&lt;size_t&gt;());\n    \n    Apple appleArray&#x5B;dimProduct]{};\n    The5DArray a5DArray{ appleArray, dimensions };\n    a5DArray&#x5B;2, 1, 2, 2, 1].seedCount += 2;\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Operatory statyczne (static operators)<\/h2>\n\n\n\n<p>Zar\u00f3wno operator wywo\u0142ania (call operator, <strong>operator()<\/strong>) jak i operator indeksu (subscript operator, <strong>operator[]<\/strong>) mog\u0105 by\u0107 zadeklarowane jako statyczne, u\u017cywaj\u0105c s\u0142owa <strong>static<\/strong> tj. nie s\u0105 zwi\u0105zane z konkretnym obiektem i wywo\u0142ywanie ich nie przekazuje niejawnego parametru obiektu (implicit object parameter). To jeden argument mniej do przekazania i dla cz\u0119sto wo\u0142anych obiekt\u00f3w funkcyjnych (function objects) mo\u017ce robi\u0107 r\u00f3\u017cnic\u0119.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nstruct OldLess\n{\n    constexpr bool operator()(const Tree&amp; tl, const Tree&amp; tr) const noexcept\n    {\n        \/\/ mo\u017cliwo\u015b\u0107 odwo\u0142ania do this\n        return tl.appleCount &lt; tr.appleCount;\n    }\n};\n\nstruct NewLess\n{\n    static constexpr bool operator()(const Tree&amp; tl, const Tree&amp; tr) noexcept\n    {\n        \/\/ BRAK mo\u017cliwo\u015bci odwo\u0142ania do this\n        return tl.appleCount &lt; tr.appleCount;\n    }\n};\n\n\/\/ wywo\u0142ania wygl\u0105daj\u0105 tak samo\nOldLess ol;\nbool v0 = ol(Tree{1}, Tree{2});\nNewLess nl;\nbool v1 = nl(Tree{1}, Tree{2}); \/\/ nie jest przekazywany wska\u017anik na nl\n<\/pre><\/div>\n\n\n<p>Je\u015bli nie masz obiektu, mo\u017cesz wywo\u0142a\u0107 albo <strong>NewLess{}(3)<\/strong> albo <strong>NewLess::operator()(3)<\/strong>, poniewa\u017c <strong>NewLess(3)<\/strong> jest wci\u0105\u017c interpretowany jako konstruktor (chocia\u017c dla typu <strong>NewLess<\/strong> nie ma odpowiadaj\u0105cego konstruktora dla tego wywo\u0142ania).<\/p>\n\n\n\n<p>Kod poni\u017cej symuluje funkcje j\u0119zyka Rust, poniewa\u017c ka\u017cda funkcja ma sw\u00f3j unikatowy typ.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ninline constexpr NewLess newLess{};\n\nnewLess(t0, t1);\nstd::ranges::sort(treeVector, newLess);\n<\/pre><\/div>\n\n\n<p>Typ por\u00f3wnuj\u0105cy (compare type) funkcji <strong>std::ranges::sort<\/strong> jest wydedukowany jako <strong>NewLess<\/strong>. Obiekt <strong>newLess<\/strong> jest przekazany przez warto\u015b\u0107 (passed by value) raz i ka\u017cde wywo\u0142anie jego operatora wywo\u0142ania <strong>operator()<\/strong> jest pozbawione przekazywania samego obiektu.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Co z lambdami?<\/h3>\n\n\n\n<p>Lambdy mog\u0105 by\u0107 teraz zadeklarowane jako <strong>static<\/strong>, co czyni ich operator wywo\u0142ania (call operator) <strong>static<\/strong>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nauto add = &#x5B;](int a, int b) static { return a + b; };\nint sum = add(1, 3); \/\/ obiekt domkni\u0119cia (closure object) sam w sobie\n                     \/\/ nie jest przekazywany do operatora wywo\u0142ania\n<\/pre><\/div>\n\n\n<p>Gdy lambda jest zadeklarowana jako <strong>static<\/strong>, nie mo\u017ce by\u0107 <strong>mutable<\/strong> \u2013 zmienno\u015b\u0107 (mutability) jest po\u0142\u0105czona ze stanem pewnego obiektu, a funkcja <strong>static<\/strong> nie jest powi\u0105zana z \u017cadnym obiektem. Lambdy s\u0105 obiektami funkcyjnymi. Wywo\u0142ywanie lambdy bez <strong>static<\/strong> niejawnie przekazuje wska\u017anik na dan\u0105 lambd\u0119. Wywo\u0142ywanie lambdy ze <strong>static<\/strong> nie przekazuje tego dodatkowego parametru. Ponadto w lambdach ze<strong> static<\/strong> nie mo\u017cna przekazywa\u0107 zmiennych &#8222;capture&#8221; (<strong>[]<\/strong> powinno by\u0107 puste).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Decay-copy jako funkcjonalno\u015b\u0107 j\u0119zyka<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Nie taki straszny rozk\u0142ad<\/h3>\n\n\n\n<p>Rozk\u0142ad (decay) to rodzaj niejawnej konwersji:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Rozk\u0142ad tablicy na wska\u017anik (array-to-pointer decay)<\/strong> odbywa si\u0119 gdy warto\u015b\u0107 (dowolnej kategorii) typu <strong>T[N]<\/strong> lub <strong>T[]<\/strong> jest niejawnie konwertowana do pr-warto\u015bci (prvalue) typu <strong>T*<\/strong>, a wynik wskazuje na pierwszy element tablicy.<\/li>\n\n\n\n<li><strong>Rozk\u0142ad funkcji na wka\u017anik (function-to-pointer decay)<\/strong> odbywa si\u0119 gdy l-warto\u015b\u0107 (lvalue) funkcji typu <strong>R(Arg&#8230;)<\/strong> jest niejawnie konwertowana do pr-warto\u015bci (prvalue) typu <strong>R(*)(Arg&#8230;)<\/strong>, a wynik wskazuje na t\u0119 funkcj\u0119. Dotyczy to tylko funkcji swobodnych (free functions) oraz statycznych funkcji sk\u0142adowych (static member functions), poniewa\u017c l-warto\u015bci odnosz\u0105ce si\u0119 do niestatycznych funkcji sk\u0142adowych nie istniej\u0105. <\/li>\n\n\n\n<li>Usuni\u0119cie referencji oraz kwalifikator\u00f3w cv (jak po zastosowaniu <strong>std::remove_cvref_t<\/strong>, np. <strong>const T&amp;&amp;<\/strong> zmienia si\u0119 w <strong>T<\/strong>).<\/li>\n<\/ul>\n\n\n\n<p>Rozk\u0142ad odbywa si\u0119 m.in. gdy wo\u0142any jest szablon funkcji, kt\u00f3rego argument jest przekazywany przez warto\u015b\u0107 (passed by value). Poniewa\u017c przekazywanie przez warto\u015b\u0107 powoduje kopi\u0119 l-warto\u015bci, to po\u0142\u0105czenie zosta\u0142o nazwane <strong>decay-copy (kopiowanie po rozk\u0142adzie)<\/strong>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate&lt;typename T&gt; void fun(T t) {}\n\nint arr0&#x5B;3];\nfun(arr0);                               \/\/ T wydedukowany jako int*\nusing T0 = std::decay_t&lt;decltype(arr0)&gt;; \/\/ T0 to int*\n\nint arr1&#x5B;3]&#x5B;4];\nfun(arr1);                               \/\/ T wydedukowany jako int(*)&#x5B;4]\nusing T1 = std::decay_t&lt;decltype(arr1)&gt;; \/\/ T1 to int(*)&#x5B;4]\n\ndouble fun2(int, int) { return {}; }     \/\/ decltype(fun2) to double(int, int)\nfun(fun2);                               \/\/ T wydedukowany jako double(*)(int, int)\nusing T2 = std::decay_t&lt;decltype(fun2)&gt;; \/\/ T2 to double(*)(int, int)\n\nconst int&amp; i = 3;\nfun(i);                                  \/\/ T wydedukowany jako int\nusing T3 = std::decay_t&lt;decltype(i)&gt;;    \/\/ T3 to int\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Specyfikator auto zn\u00f3w upraszcza<\/h3>\n\n\n\n<p>Od C++23 mo\u017cna u\u017cywa\u0107 nowej jawnej konwersji typu: <strong>auto(x)<\/strong> oraz <strong>auto{x}<\/strong>. W tej konwersji <strong>auto<\/strong> jest zast\u0119powane przez <strong>decltype(var)<\/strong>, gdzie <strong>var<\/strong> jest hipotetyczn\u0105 zmienn\u0105 zadeklarowan\u0105 jako odpowiednio <strong>auto var(x);<\/strong> lub <strong>auto var{x};<\/strong>, a wynik jest zawsze pr-warto\u015bci\u0105 (prvalue) wydedukowanego typu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntemplate&lt;typename T&gt; void fun(T&amp;&amp; t) {}\n\nint arr0&#x5B;3];\nfun(arr0);                                     \/\/ T wydedukowany jako int(&amp;)&#x5B;3]\nfun(auto{arr0});                               \/\/ T wydedukowany jako int*\nusing T0 = int*; fun(T0{arr0});                \/\/ jak wy\u017cej\n\nint arr1&#x5B;3]&#x5B;4];\nfun(arr1);                                     \/\/ T wydedukowany jako int(&amp;)&#x5B;3]&#x5B;4]\nfun(auto{arr1});                               \/\/ T wydedukowany jako int(*)&#x5B;4]\nusing T1 = int(*)&#x5B;4]; fun(T1{arr1});           \/\/ jak wy\u017cej\n\ndouble fun2(int, int) { return {}; }           \/\/ decltype(fun2) to double(int, int)\nfun(fun2);                                     \/\/ T wydedukowany jako double(&amp;)(int, int)\nfun(auto{fun2});                               \/\/ T wydedukowany jako double(*)(int, int)\nusing T2 = double(*)(int, int); fun(T2{fun2}); \/\/ jak wy\u017cej\n\nconst int&amp; i = 3;\nfun(i);                                        \/\/ T wydedukowany jako const int&amp;\nfun(auto{i});                                  \/\/ T wydedukowany jako int\nusing T3 = int; fun(T3{i});                    \/\/ jak wy\u017cej\n<\/pre><\/div>\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce chcemy zast\u0105pi\u0107 wszystkie elementy zakresu (range), kt\u00f3re s\u0105 r\u00f3wne pierwszemu elementowi:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid replaceAllEqualToFirst(auto&amp; rng, const auto&amp; newValue)\n{\n    std::ranges::replace(rng, rng.front(), newValue);\n}\n\nApple getBetterApple() { return {}; }\n\nstd::vector&lt;Apple&gt; appleVec;\nreplaceAllEqualToFirst(appleVec, getBetterApple());\n<\/pre><\/div>\n\n\n<p>To nie zadzia\u0142a tak, jak za\u0142o\u017cyli\u015bmy, poniewa\u017c po zast\u0105pieniu pierwszego elementu nowym, nast\u0119pne elementy s\u0105 por\u00f3wnywane z podmienionym pierwszym elementem. By to naprawi\u0107 nale\u017cy stworzy\u0107 kopi\u0119:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid replaceAllEqualToFirst(auto&amp; rng, const auto&amp; newValue)\n{\n    auto firstElement = rng.front();\n    std::ranges::replace(rng, firstElement, newValue);\n}\n<\/pre><\/div>\n\n\n<p>Jak mo\u017cna to upro\u015bci\u0107? Mo\u017cna pomin\u0105\u0107 zmienn\u0105 lokaln\u0105, ale b\u0119dzie to wymaga\u0107 dedukcji typu:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid replaceAllEqualToFirst(auto&amp; rng, const auto&amp; newValue)\n{\n    using T = std::decay_t&lt;decltype(rng.front())&gt;;\n    std::ranges::replace(rng, T(rng.front()), newValue);\n}\n<\/pre><\/div>\n\n\n<p>Podsumowuj\u0105c \u2013 nie za bardzo pro\u015bciej. Rzutowanie <strong>auto<\/strong> na ratunek:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid replaceAllEqualToFirst(auto&amp; rng, const auto&amp; newValue)\n{\n    std::ranges::replace(rng, auto(rng.front()), newValue);\n}\n<\/pre><\/div>\n\n\n<p><strong>auto<\/strong> dedukuje typ i tworzy kopi\u0119 obiektu, na przyk\u0142adzie:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nint arr&#x5B;] = { -1, 0, -1, 2, 3, -1 };\nreplaceAllEqualToFirst(arr, -2);\n<\/pre><\/div>\n\n\n<p>kopiuje pierwszy element (<strong>int(rng.front())<\/strong>, czyli <strong>-1<\/strong>) i wykorzystuje t\u0119 tymczasow\u0105 kopi\u0119 do por\u00f3wnywania w <strong>std::ranges::replace<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Przygotowanie na przysz\u0142o\u015b\u0107<\/h2>\n\n\n\n<p>Bior\u0105c stan na 25.09.2024, cz\u0119\u015b\u0107 zmian wci\u0105\u017c nie zosta\u0142a zaimplementowana w MSVC, podczas gdy GCC i Clang s\u0105 zaktualizowane (<a href=\"https:\/\/en.cppreference.com\/w\/cpp\/23\" target=\"_blank\" aria-label=\" (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\" rel=\"nofollow\" >obecny stan na cppreference<\/a>). Nawet je\u015bli nie mo\u017cesz obecnie u\u017cywa\u0107 tych funkcjonalno\u015bci, posiadanie wst\u0119pnej wiedzy na ich temat b\u0119dzie przydatne w przysz\u0142o\u015bci.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>Je\u015bli interesuje Ci\u0119 j\u0119zyk C++, zajrzyj r\u00f3wnie\u017c <a href=\"https:\/\/sii.pl\/blog\/wyszukiwarka\/C%2B%2B\/\" target=\"_blank\" aria-label=\"do innych artyku\u0142\u00f3w naszych ekspert\u00f3w (opens in a new tab)\" rel=\"noreferrer noopener\" class=\"ek-link\">do innych artyku\u0142\u00f3w naszych ekspert\u00f3w<\/a>. <\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;29431&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;8&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 8)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Nowe funkcjonalno\u015bci j\u0119zyka w C++23&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 8)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>C++ jest j\u0119zykiem, kt\u00f3ry wci\u0105\u017c ewoluuje. Od C++11 co 3 lata publikowana jest nowa wersja standardu. Publikacje proponuj\u0105ce zmiany (aktualna &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/nowe-funkcjonalnosci-jezyka-w-c23\/\">Continued<\/a><\/p>\n","protected":false},"author":680,"featured_media":29438,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2737,1528,1512,563,1058],"class_list":["post-29431","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-c23","tag-jezyk-programowania","tag-poradnik","tag-embedded","tag-cpp"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2024\/11\/Nowe-funkcjonalnosci-jezyka-w-C23.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/29431"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/680"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=29431"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/29431\/revisions"}],"predecessor-version":[{"id":29446,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/29431\/revisions\/29446"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/29438"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=29431"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=29431"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=29431"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}