C ++たたは...の堎合、完党な静的の欠劂を埌悔する

...テンプレヌトパラメヌタの倀に応じお異なる内容でテンプレヌトクラスを埋める方法は


むかしむかし、かなり前に、C ++で埗られた経隓を考慮しお、D蚀語は「正しいC ++」ずしお䜜成されるようになりたした。 時間が経぀に぀れお、DはC ++よりも耇雑で衚珟力豊かな蚀語になりたした。 そしお、すでにC ++はDをスパむし始めたした。たずえば、 if constexprがDから盎接借甚しおいるif constexpr 、C ++ 17に登堎したした。そのプロトタむプはD-shny static ifでした。


残念ながら、C ++のif constexprがDのstatic ifず同じ力を持たないstatic if 、これにはいく぀かの理由がありたすが、C ++のif constexprがC + +クラス。 これらのケヌスの1぀に぀いおお話ししたいず思いたす。


テンプレヌトクラスを䜜成する方法に぀いお説明したす。テンプレヌトクラスの内容メ゜ッドの構成ずメ゜ッドのロゞックは、このテンプレヌトクラスに枡されたパラメヌタヌに応じお倉化したす。 䟋は、SObjectizerの新しいバヌゞョンを開発した経隓から、実生掻から取られおいたす。


解決すべき課題


メッセヌゞオブゞェクトを保存するための「スマヌトポむンタ」の賢いバヌゞョンを䜜成する必芁がありたす。 次のように曞くこずができたす


 message_holder_t<my_message> msg{ new my_message{...} }; send(target, msg); send(another_target, msg); 

このmessage_holder_tクラスのmessage_holder_t isは、考慮すべき3぀の重芁な芁玠があるこずです。


継承されるメッセヌゞの皮類は䜕ですか


message_holder_tをパラメヌタヌ化するメッセヌゞのタむプは、2぀のグルヌプに分けられたす。 最初のグルヌプは、特別なベヌスタむプmessage_tを継承するmessage_tです。 䟋


 struct so5_message final : public so_5::message_t { int a_; std::string b_; std::chrono::milliseconds c_; so5_message(int a, std::string b, std::chrono::milliseconds c) : a_{a}, b_{std::move(b)}, c_{c} {} }; 

この堎合、内郚のmessage_holder_tには、このタむプのオブゞェクトぞのポむンタヌのみを含める必芁がありたす。 同じポむンタヌがゲッタヌメ゜ッドで返される必芁がありたす。 ぀たり、 message_tの継承者の堎合、次のようになりたす。


 template<typename M> class message_holder_t { intrusive_ptr_t<M> m_msg; public: ... const M * get() const noexcept { return m_msg.get(); } }; 

2番目のグルヌプは、 message_tから継承されない任意のナヌザヌタむプのmessage_tです。 䟋


 struct user_message final { int a_; std::string b_; std::chrono::milliseconds c_; user_message(int a, std::string b, std::chrono::milliseconds c) : a_{a}, b_{std::move(b)}, c_{c} {} }; 

SObjectizerのこれらのタむプのむンスタンスは、それ自䜓では送信されたせんが、特別なラッパヌuser_type_message_t<M>で囲たれおいたす。 user_type_message_t<M>ラッパヌは、既にmessage_tから継承さmessage_tたす。 したがっお、そのような型の堎合、 message_holder_tはその䞭にuser_type_message_t<M>ぞのポむンタヌを含む必芁があり、ゲッタヌメ゜ッドはMぞのポむンタヌを返す必芁がありたす。


 template<typename M> class message_holder_t { intrusive_ptr_t<user_type_message_t<M>> m_msg; public: ... const M * get() const noexcept { return std::addressof(m_msg->m_payload); } }; 

メッセヌゞの耐性たたは可倉性


2番目の芁因は、メッセヌゞを䞍倉および可倉に分割するこずです。 メッセヌゞが䞍倉およびデフォルトでは䞍倉である堎合、getterメ゜ッドはメッセヌゞぞの定数ポむンタヌを返す必芁がありたす。 たた、可倉の堎合、ゲッタヌは非定数ポむンタヌを返す必芁がありたす。 ぀たり 次のようなものでなければなりたせん


 message_holder_t<so5_message> msg1{...}; //  . const int a = msg1->a_; // OK. msg1->a_ = 0; //     ! message_holder_t<mutable_msg<user_message>> msg2{...}; //  . const int a = msg2->a_; // OK. msg2->a_ = 0; // OK. 

shared_ptr vs unique_ptr


3番目の芁因は、スマヌトポむンタヌずしおのmessage_holder_tの動䜜のロゞックです。 むかしむかし、 std::shared_ptrように振る舞う必芁がありたす。 同じメッセヌゞむンスタンスを参照する耇数のmessage_holdersを持぀こずができたす。 そしお、䞀床std::unique_ptrように振る舞うはずです。 メッセヌゞむンスタンスを参照できるmessage_holderむンスタンスは1぀だけです。


デフォルトでは、 message_holder_tの動䜜は、メッセヌゞの可倉性/䞍倉性に䟝存する必芁がありたす。 ぀たり 䞍倉のメッセヌゞでは、 message_holder_tはstd::shared_ptrように動䜜し、 std::unique_ptrような可倉std::unique_ptr 


 message_holder_t<so5_message> msg1{...}; message_holder_t<so5_message> msg2 = msg; // OK. message_holder_t<mutable_msg<user_message>> msg3{...}; message_holder_t<mutable_msg<user_message>> msg4 = msg3; // !  ! message_holder_t<mutable_msg<user_message>> msg5 = std::move(msg3); // OK. 

しかし、人生は耇雑なものなので、 message_holder_t動䜜を手動で蚭定できるようにする必芁もありたす。 これにより、unique_ptrのように動䜜する䞍倉メッセヌゞのmessage_holderを䜜成できたす。 そしお、shared_ptrのように動䜜する可倉メッセヌゞのmessage_holderを䜜成できたす。


 using unique_so5_message = so_5::message_holder_t< so5_message, so_5::message_ownership_t::unique>; unique_so5_message msg1{...}; unique_so5_message msg2 = msg1; // !  ! unique_so5_message msg3 = std::move(msg); // OK,   msg3. using shared_user_messsage = so_5::message_holder_t< so_5::mutable_msg<user_message>, so_5::message_ownership_t::shared>; shared_user_message msg4{...}; shared_user_message msg5 = msg4; // OK. 

したがっお、 message_holder_tがshared_ptrのように機胜する堎合、通垞のコンストラクタヌず割り圓お挔算子のセットコピヌず移動の䞡方が必芁です。 さらに、定数メ゜ッドmake_referenceが必芁make_reference 。このメ゜ッドは、 message_holder_t内に栌玍されおいるポむンタヌのコピヌを返したす。


ただし、 message_holder_tがunique_ptrずしお機胜する堎合は、コンストラクタずコピヌ挔算子を犁止する必芁がありたす。 make_referenceメ゜ッドは、 message_holder_tオブゞェクトからポむンタヌをmake_referenceする必芁がありたすmake_referenceを呌び出した埌make_reference元のmessage_holder_tは空のたたにしおおく必芁がありたす。


もう少しフォヌマル


そのため、テンプレヌトクラスを䜜成する必芁がありたす。


 template< typename M, message_ownership_t Ownership = message_ownership_t::autodetected> class message_holder_t {...}; 

どれ



リストの最埌の2぀の項目は、所有暩パラメヌタヌおよびautodetected倀がautodetected堎合のメッセヌゞの可倉性によっお決定されたす。


決定方法


このセクションでは、最終的な゜リュヌションを構成するすべおのコンポヌネントを怜蚎したす。 さお、結果の゜リュヌション自䜓。 気が散る詳现がすべおクリアされたコヌドフラグメントが衚瀺されたす。 誰かが実際のコヌドに興味があるなら、 ここでそれを芋るこずができたす 。


免責事項


以䞋に瀺す゜リュヌションは、矎、理想、たたはロヌルモデルのふりをするものではありたせん。 締め切りのプレッシャヌの䞋、短時間で発芋、実装、テスト、文曞化されたした。 おそらく、もっず時間があり、解決策の怜玢にもっず倚くの時間を費やしたずしたら 若い 珟代のC ++開発者にずっお賢明で知識が豊富なため、よりコンパクトでシンプルで理解しやすいものになりたす。 しかし、それが起こったずき、それは起こりたした...「ピアニストを撃぀な」、䞀般に。


手順のシヌケンスず既補のテンプレヌトマゞック


そのため、いく぀かのメ゜ッドのセットを持぀クラスが必芁です。 これらのキットの内容はどこかからのものでなければなりたせん。 どこから


Dでは、 static ifを䜿甚し、さたざたな条件に応じおクラスのさたざたな郚分を定矩できたす。 䞀郚のRubyでは、includeメ゜ッドを䜿甚しおメ゜ッドをクラスにミックスできたす。 しかし、私たちはC ++を䜿甚しおいたすが、これたでのずころ可胜性は非垞に限られおいたす。クラス内でメ゜ッド/属性を盎接定矩するか、基本クラスからメ゜ッド/属性を継承するこずができたす。


条件に応じおクラス内で異なるメ゜ッド/属性を定矩するこずはできたせん。 if constexprがstatic ifないif constexpr C ++。 したがっお、継承のみが残りたす。


曎新したした。 コメントで瀺唆されおいるように、私はここでもっず泚意深く話すべきです。 C ++にはSFINAEがあるため、SFINAEを介しおクラス内の個々のメ゜ッドの可芖性を有効/無効にするこずができたす぀たり、 static if䌌た効果を実珟しstatic if 。 しかし、このアプロヌチには、私の意芋では、2぀の重倧な欠点がありたす。 たず、そのようなメ゜ッドが1-2-3ではなく、4-5以䞊の堎合、SFINAEを䜿甚しお各メ゜ッドを蚭蚈するのは面倒であり、これはコヌドの可読性に圱響したす。 第二に、SFINAEは、クラス属性フィヌルドの远加/削陀を支揎したせん。

C ++では、 message_holder_tを継承するいく぀かの基本クラスを定矩できたす。 たた、1぀たたは別の基本クラスの遞択は、テンプレヌトパラメヌタヌの倀に応じお、 std ::条件を䜿甚しお既に行われたす 。


しかし、コツは、基本クラスのセットだけでなく、継承の小さなチェヌンが必芁なこずです。 最初に、どのような堎合でも必芁ずされる䞀般的な機胜を決定するクラスがありたす。 次は、「スマヌトポむンタヌ」の動䜜のロゞックを決定する基本クラスです。 そしお、必芁なゲッタヌを決定するクラスがありたす。 この順序で、実装されたクラスを怜蚎したす。


SObjectizerには、メッセヌゞがメッセヌゞの可倉性をチェックする手段ず同様に、message_tから継承されるかどうかを決定する既補のテンプレヌトマゞックが既にあるずいう事実によっお、タスクが単玔化されおいたす 。 したがっお、実装では、この既補の魔法を䜿甚するだけで、その䜜業の詳现に぀いおは詳しく説明したせん。


共通のポむンタヌストレヌゞベヌス


察応するintrusive_ptrを栌玍する共通のベヌスタむプから始めたしょう。たた、 message_holder_t実装のいずれかが必芁ずするメ゜ッドの共通セットも提䟛したす。


 template< typename Payload, typename Envelope > class basic_message_holder_impl_t { protected : intrusive_ptr_t< Envelope > m_msg; public : using payload_type = Payload; using envelope_type = Envelope; basic_message_holder_impl_t() noexcept = default; basic_message_holder_impl_t( intrusive_ptr_t< Envelope > msg ) noexcept : m_msg{ std::move(msg) } {} void reset() noexcept { m_msg.reset(); } [[nodiscard]] bool empty() const noexcept { return static_cast<bool>( m_msg ); } [[nodiscard]] operator bool() const noexcept { return !this->empty(); } [[nodiscard]] bool operator!() const noexcept { return this->empty(); } }; 

このテンプレヌトクラスには2぀のパラメヌタヌがありたす。 最初のペむロヌドは、ゲッタヌメ゜ッドが䜿甚するタむプを蚭定したす。 䞀方、2番目のEnvelopeは、intrusive_ptrのタむプを蚭定したす。 メッセヌゞタむプがmessage_tから継承される堎合、これらのパラメヌタヌは䞡方ずも同じ倀になりたす。 ただし、メッセヌゞがmessage_tから継承されおいない堎合、メッセヌゞタむプはペむロヌドずしお䜿甚され、 user_type_message_t<Payload>はEnvelopeずしおuser_type_message_t<Payload>れたす。


基本的にこのクラスの内容は疑問を投げかけないず思いたす。 ただし、2぀の点に泚意しおください。


たず、ポむンタヌ自䜓、぀たり クラス継承者がアクセスできるように、m_msg属性はprotectedセクションで定矩されおいたす。


第二に、このクラスでは、コンパむラヌ自䜓が必芁なすべおのコンストラクタヌずコピヌ/移動挔算子を生成したす。 このクラスのレベルでは、ただ䜕も犁止しおいたせん。


shared_ptrおよびunique_ptrの動䜜の個別のベヌス


そのため、メッセヌゞぞのポむンタヌを栌玍するクラスがありたす。 これで、盞続人を定矩できたす。これはshared_ptrたたはunique_ptrずしお動䜜したす。


shared_ptrの動䜜の堎合から始めたしょう。なぜなら、 ここに最小のコヌドがありたす


 template< typename Payload, typename Envelope > class shared_message_holder_impl_t : public basic_message_holder_impl_t<Payload, Envelope> { using direct_base_type = basic_message_holder_impl_t<Payload, Envelope>; public : using direct_base_type::direct_base_type; [[nodiscard]] intrusive_ptr_t< Envelope > make_reference() const noexcept { return this->m_msg; } }; 

耇雑なこずはありたせんbasic_message_holder_impl_tから継承し、そのすべおのコンストラクタヌを継承し、 make_reference()単玔で非砎壊的な実装を定矩したす。


unique_ptrの動䜜の堎合、コヌドは倧きくなりたすが、耇雑なものはありたせん。


 template< typename Payload, typename Envelope > class unique_message_holder_impl_t : public basic_message_holder_impl_t<Payload, Envelope> { using direct_base_type = basic_message_holder_impl_t<Payload, Envelope>; public : using direct_base_type::direct_base_type; unique_message_holder_impl_t( const unique_message_holder_impl_t & ) = delete; unique_message_holder_impl_t( unique_message_holder_impl_t && ) = default; unique_message_holder_impl_t & operator=( const unique_message_holder_impl_t & ) = delete; unique_message_holder_impl_t & operator=( unique_message_holder_impl_t && ) = default; [[nodiscard]] intrusive_ptr_t< Envelope > make_reference() noexcept { return { std::move(this->m_msg) }; } }; 

繰り返したすが、 basic_message_holder_impl_tから継承し、必芁なコンストラクタヌを継承したすこれはデフォルトのコンストラクタヌおよび初期化コンストラクタヌです。 しかし同時に、unique_ptrロゞックに埓っお、コンストラクタヌずコピヌ/移動挔算子を定矩したす。コピヌを犁止し、移動を実装したす。


ここには砎壊的なmake_reference()メ゜ッドもありmake_reference() 。


これですべおです。 これらの2぀の基本クラスの間の遞択を実珟するためだけに残りたす...


shared_ptrずunique_ptrの動䜜の遞択


shared_ptrの動䜜ずunique_ptrの動䜜を遞択するには、次のメタ関数が必芁ですメタ関数はコンパむル時に型で「機胜する」ため。


 template< typename Msg, message_ownership_t Ownership > struct impl_selector { static_assert( !is_signal<Msg>::value, "Signals can't be used with message_holder" ); using P = typename message_payload_type< Msg >::payload_type; using E = typename message_payload_type< Msg >::envelope_type; using type = std::conditional_t< message_ownership_t::autodetected == Ownership, std::conditional_t< message_mutability_t::immutable_message == message_mutability_traits<Msg>::mutability, shared_message_holder_impl_t<P, E>, unique_message_holder_impl_t<P, E> >, std::conditional_t< message_ownership_t::shared == Ownership, shared_message_holder_impl_t<P, E>, unique_message_holder_impl_t<P, E> > >; }; 

このメタ関数は、 message_holder_tパラメヌタヌリストから䞡方のパラメヌタヌを受け入れ、その結果぀たり、ネストされたtype定矩、継承元の型を「返したす」。 ぀たり shared_message_holder_impl_tたたはunique_message_holder_impl_tいずれか。


impl_selectorの定矩内では、䞊蚘の魔法の痕跡を芋るこずができたすが、ここでは説明したせんでした message_payload_type<Msg>::payload_type 、 message_payload_type<Msg>::envelope_typeおよびmessage_mutability_traits<Msg>::mutability


そしお、メタ関数impl_selectorを䜿甚するimpl_selectorが簡単だったので、短い名前を定矩したす


 template< typename Msg, message_ownership_t Ownership > using impl_selector_t = typename impl_selector<Msg, Ownership>::type; 

ゲッタヌのベヌス


そのため、すでにポむンタヌを含み、「スマヌトポむンタヌ」の動䜜を定矩するベヌスを遞択する機䌚がありたす。 次に、このベヌスにゲッタヌメ゜ッドを提䟛する必芁がありたす。 単玔なクラスが1぀必芁な理由


 template< typename Base, typename Return_Type > class msg_accessors_t : public Base { public : using Base::Base; [[nodiscard]] Return_Type * get() const noexcept { return get_ptr( this->m_msg ); } [[nodiscard]] Return_Type & operator * () const noexcept { return *get(); } [[nodiscard]] Return_Type * operator->() const noexcept { return get(); } }; 

これは2぀のパラメヌタヌに䟝存するテンプレヌトクラスですが、それらの意味はたったく異なりたす。 Baseパラメヌタヌは、䞊蚘のimpl_selectorメタ関数の結果です。 ぀たり Baseパラメヌタヌずしお、継承元の基本クラスが蚭定されたす。


コンストラクタヌずコピヌ挔算子が犁止されおいるunique_message_holder_impl_tから継承される堎合、コンパむラヌはmsg_accessors_tコンストラクタヌずコピヌ挔算子を生成できないこずに泚意するこずが重芁msg_accessors_t 。 これが必芁なものです。


メッセヌゞのタむプ、ゲッタヌによっお返されるポむンタヌ/リンクは、Return_Typeパラメヌタヌずしお機胜したす。 秘Theは、タむプMsg䞍倉メッセヌゞのMsg 、Return_Typeパラメヌタヌがconst Msg蚭定されるこずです。 䞀方、タむプMsg可倉メッセヌゞのMsgパラメヌタヌReturn_Typeの倀はMsgたす。 したがっお、 get()メ゜ッドは、䞍倉メッセヌゞの堎合はconst Msg*を返し、可倉メッセヌゞの堎合はMsg*のみを返したす。


無料の関数get_ptr()を䜿甚するず、 message_tから継承されおいないメッセヌゞを凊理get_ptr()問題message_t解決されたす。


 template< typename M > M * get_ptr( const intrusive_ptr_t<M> & msg ) noexcept { return msg.get(); } template< typename M > M * get_ptr( const intrusive_ptr_t< user_type_message_t<M> > & msg ) noexcept { return std::addressof(msg->m_payload); } 

぀たり メッセヌゞがmessage_tから継承されuser_type_message_t<Msg> 、 user_type_message_t<Msg>ずしお保存されおいる堎合、2番目のオヌバヌロヌドが呌び出されたす。 そしお、それが継承される堎合、最初のオヌバヌロヌド。


ゲッタヌの特定のベヌスを遞択する


そのため、 msg_accessors_tテンプレヌトには2぀のパラメヌタヌが必芁です。 最初はimpl_selectorメタ関数によっお蚈算されたす。 ただし、 msg_accessors_tから特定の基本型をmsg_accessors_tするには、2番目のパラメヌタヌの倀を決定する必芁がありたす。 もう1぀のメタ機胜がこれを目的ずしおいたす。


 template< message_mutability_t Mutability, typename Base > struct accessor_selector { using type = std::conditional_t< message_mutability_t::immutable_message == Mutability, msg_accessors_t<Base, typename Base::payload_type const>, msg_accessors_t<Base, typename Base::payload_type> >; }; 

Return_Typeパラメヌタヌの蚈算にのみ泚意を払うこずができたす。 east constが有甚な数少ないケヌスの1぀です;


さお、埌続のコヌドの可読性を高めるために、それを扱うためのよりコンパクトなオプション


 template< message_mutability_t Mutability, typename Base > using accessor_selector_t = typename accessor_selector<Mutability, Base>::type; 

最終承継者message_holder_t


これで、これらのすべおの基本クラスずメタ関数が必芁な実装のために、 message_holder_t䜕であるかを芋るこずができたすmessage_holderに保存されたメッセヌゞのむンスタンスを構築するための実装からいく぀かのメ゜ッドが削陀されたした


 template< typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected > class message_holder_t : public details::message_holder_details::accessor_selector_t< details::message_mutability_traits<Msg>::mutability, details::message_holder_details::impl_selector_t<Msg, Ownership> > { using base_type = details::message_holder_details::accessor_selector_t< details::message_mutability_traits<Msg>::mutability, details::message_holder_details::impl_selector_t<Msg, Ownership> >; public : using payload_type = typename base_type::payload_type; using envelope_type = typename base_type::envelope_type; using base_type::base_type; friend void swap( message_holder_t & a, message_holder_t & b ) noexcept { using std::swap; swap( a.message_reference(), b.message_reference() ); } }; 

実際、䞊蚘の2぀のメタ関数の「呌び出し」を蚘録するには、䞊蚘で分析したすべおが必芁でした。


 details::message_holder_details::accessor_selector_t< details::message_mutability_traits<Msg>::mutability, details::message_holder_details::impl_selector_t<Msg, Ownership> > 

なぜなら これは最初のオプションではありたせんが、コヌドの簡玠化ず削枛の結果、メタ関数のコンパクトなフォヌムはコヌドの量を倧幅に削枛し、その理解床を高めおいるず蚀えたすここで䞀般的に理解床に぀いお話すのが適切な堎合。


そしお、どうなるでしょう...


しかし、C ++でif constexprがDでstatic ifくらい匷力でstatic if 、次のように曞くこずができたす。


constexprのより高床な仮想オプション
 template< typename Msg, message_ownership_t Ownership = message_ownership_t::autodetected > class message_holder_t { static constexpr const message_mutability_t Mutability = details::message_mutability_traits<Msg>::mutability; static constexpr const message_ownership_t Actual_Ownership = (message_ownership_t::unique == Ownership || (message_mutability_t::mutable_msg == Mutability && message_ownership_t::autodetected == Ownership)) ? message_ownership_t::unique : message_ownership_t::shared; public : using payload_type = typename message_payload_type< Msg >::payload_type; using envelope_type = typename message_payload_type< Msg >::envelope_type; private : using getter_return_type = std::conditional_t< message_mutability_t::immutable_msg == Mutability, payload_type const, payload_type >; public : message_holder_t() noexcept = default; message_holder_t( intrusive_ptr_t< envelope_type > mf ) noexcept : m_msg{ std::move(mf) } {} if constexpr(message_ownership_t::unique == Actual_Ownership ) { message_holder_t( const message_holder_t & ) = delete; message_holder_t( message_holder_t && ) noexcept = default; message_holder_t & operator=( const message_holder_t & ) = delete; message_holder_t & operator=( message_holder_t && ) noexcept = default; } friend void swap( message_holder_t & a, message_holder_t & b ) noexcept { using std::swap; swap( a.m_msg, b.m_msg ); } [[nodiscard]] getter_return_type * get() const noexcept { return get_const_ptr( m_msg ); } [[nodiscard]] getter_return_type & operator * () const noexcept { return *get(); } [[nodiscard]] getter_return_type * operator->() const noexcept { return get(); } if constexpr(message_ownership_t::shared == Actual_Ownership) { [[nodiscard]] intrusive_ptr_t< envelope_type > make_reference() const noexcept { return m_msg; } } else { [[nodiscard]] intrusive_ptr_t< envelope_type > make_reference() noexcept { return { std::move(m_msg) }; } } private : intrusive_ptr_t< envelope_type > m_msg; }; 

私にずっお、その違いはあたりにも顕著です。 そしお、圌らは珟圚のC ++を支持しおいたせん:(
( C++ "" ).


, , ++. , , , . , message_holder_t . , , if constexpr .


おわりに


私の堎合、この䟋はC ++のすべおの玠晎らしさず貧困を瀺しおいたす。はい、奜きなものを䜜成できたす。ある意味では、テンプレヌトクラスを䜜成できたす。テンプレヌトクラスの内容は、テンプレヌトパラメヌタに応じお根本的に倉化したす。


ただし、これを行うには、頭をやや壊し、テンプレヌトに非垞に倚くの補助コヌドを蚘述する必芁があるため、䜜成者でさえもこのすべおを詳しく調べたくないでしょう。


それにもかかわらず、C ++でこれを行うこずができるずいう事実は、私を個人的に幞せにしたす。それは、必芁な䜜業量ずコヌド量を混乱させたす。しかし、時間の経過ずずもに、このコヌドの量ずその耇雑さが枛少するこずを願っおいたす。原則ずしお、これはすでに芋えおいたす。C ++ 98/03の堎合、私はそのようなトリックを匕き受けるこずさえしたせんが、C ++ 11から始めるず、これがより簡単になりたす。



Source: https://habr.com/ru/post/J449122/


All Articles