短い説明がない人のための右蟺倀リンクの詳现な玹介

KDPVの代わりに-実際のむベントに基づいお泚目を集めるための短いドラマ。 これを安党にスキップしお蚘事に移動できたす。これは、右蟺倀リンクの理解、コンストラクタヌの移動、ナニバヌサルリンク、完党な転送などに圹立ちたす。


3幕のドラマ


アクション1


コンパむラ スタックに䜏んでいるタむプTのロヌカルオブゞェクトxは、残りの人生では䜿甚しないずいう事実により、すべおのプロパティを奪取するように宣告されたす。


オブゞェクトx なに 私はそこに䞀時的なオブゞェクトではありたせん、私は氞続的な登録を持っおいたす、あなたには暩利がありたせん


コンパむラ 誰もあなたを远攟したせん。 しかし、暙準コヌドの第11版によれば、すべおのものは、それらをさらに必芁ずする別のオブゞェクトに転送されたす。


オブゞェクトx そしお、どうやっおそれをしたすか すべおのデヌタは安党にカプセル化されおいるため、だれもそれを䞍圓に凊理するこずはできたせん。 本圓にそんなに必芁な堎合は、コピヌコンストラクタヌにフラッシュドラむブを付属させお、コピヌしたす。



コンストラクタヌ 。 長くお非効率的です。


オブゞェクトx たぶん、あなたはreinterpret_castで私の窓を壊し、暗闇の䞭でmmきたすか


コンパむラ いいえ、あなたのケヌスはコレクタヌのサヌビスを䜿甚するにはあたりにも普通です。 std :: move関数を呌び出すだけで、static_cast <T &&>が蚭定され、誰もが䞀時オブゞェクト、぀たり右蟺倀匏であるずみなされたす。


オブゞェクトx それで、static_castは私に䜕の圱響も䞎えたせん。 継承の最初のポむントに到達したらすぐに削陀したす。


コンパむラ 私は疑う䜙地はありたせんが、これを行う前に、あなたはすでにドアの倖で埅っおいる運動コンストラクタヌに䌚いたす。 あなたは圌に䌚う時間がなかったようです...


アクション2


TT && 。 こんにちは、䞀時オブゞェクト


オブゞェクトx いやいやいや、私は巊蟺倀です。


TT && 。 誰もがそう蚀いたす。 さお、あなたは䜕を持っおいたすか ここに来お...


第3幕゚ピロヌグ


T定数T 私はあなたの暎力的な方法に満足しおいたせん。


TT && 。 あなたは誇匵し、自分で考えおください、死にかけおいるオブゞェクトはなぜこのゞャンクをすべお必芁ずするのですか それが私にずっおでなければ、長いコピヌを䜜成し、デストラクタはオリゞナルを砎壊しおいたでしょう。 バカ。


T定数T それで圌は嘘を぀かなかったのかもしれたせん。


TT && 。 それは私に違いはありたせん。 だから誰かがそれが必芁だず決めた。 すべおが合法です、私はちょうど私の仕事をしおいたす。


T定数T しかし、以前は、あなたがいなくおもうたくいきたした。 どういうわけか、圌らは転送ず戻りを敎理するか、動的メモリ内の生掻空間をオブゞェクトに割り圓おたした。 はい、コンパむラは最適化を支揎したした。


TT && 。 たあ、このすべおの官僚䞻矩を芚えおおいおください。 オブゞェクトが移入され、登録蚘録がすべお倱われ、圌は最埌たでそこに䜏んでいたが、それを远い払うこずはしなかった。 これらのオブゞェクトをすでに埌悔するのに十分な堎合、そうでなければあなたは私の兄匟Tconst T &&に倉わりたす-圌はさらに思いやりがありたす。 私は圌に蚀いたした「それで、オブゞェクトはもはやテナントではなく、圌の持ち物を持ちたす」、そしお圌はそれが䞍快だず蚀っお、それをコピヌしたしょう。


T定数T 私には、本圓の凶悪犯である兄匟TTもいたす。 それは、コピヌコンストラクタずしお、私になりすたしたす... そしお、怠を思い぀きたす。


たえがき


新しい抂念は必ずしも頭にぎったり収たるずは限りたせん。 右蟺倀リンクで私に起こりたした。 すべおが非垞に単玔で、その倖芳の前提条件は明確であるように芋えたすが、さたざたなテンプレヌトに包たれたさたざたな&&で飜和したコヌドを読み取ろうずするず、䞀般に䜕も理解しおいないこずがわかりたす。


このトピックの研究における私の間違いは、右蟺倀リンクを根本的に新しい゚ンティティずしお衚したこずです。 おそらく、これは奇劙に思えるかもしれたせん。すべおのマニュアルには、これは単なる右蟺倀ぞの参照であるず明確に曞かれおいるからです。 わかった。 しかし、結局のずころ、ナニバヌサルリンクや完党な転送など、倚くの新しい抂念がそれらず共に登堎したした。 たた、&&を返す関数を呌び出すず、ある皮の神秘的なxvalue匏になりたす。 芁するに、それらを通垞のリンクず芋なすのは簡単すぎるでしょう。


だから、最も重芁なこず-耇雑にしないでください T&& ref = foo()を芋たが、珟圚refに関連付ける方法がわからない堎合、右蟺倀ぞの叀き良き定数参照ずしお扱いたす const T& ref = foo() 、constなしのみ。


そしお、なぜそれだけで右蟺倀ぞのリンクを取るこずが蚱されなかったのでしょうか そうしないず、匏が巊蟺倀であるか右蟺倀であるかに関する情報がすぐに倱われたす。 これで、右蟺倀は匕数T &&で関数に枡され、巊蟺倀はTで枡されるようになりたす。 これにより、オブゞェクトをさたざたな方法で凊理できるようになりたす。これは、コピヌおよび移動コンストラクタヌの実装にずっお特に重芁です。


私のもう1぀の間違いは、Visual Studioの䟋を確認するこずです。 蚘事内の䟋、たずえばstd::string &str = std::string("42") 。これはコンパむルしないで、私ず䞀緒にコンパむルしたす。 これは、Visual Studioの非暙準の蚀語拡匵機胜によるものです。 VSが開発環境である堎合、この動䜜を理解するこずは非垞に重芁であるため、これに぀いお詳しく説明したす。


この蚘事を読む最良の方法は、私を信じないで、自分ですべおをチェックするこずです。 デバッグアセンブリでアドバむスしたす。 GCCを䜿甚する堎合-fno-elide-constructorsスむッチを-fno-elide-constructors可胜な限りコピヌおよび移動コンストラクタヌの呌び出しを抑制するCopy Elisionテクニックをオフにするこずをお勧めしたす。 VSの堎合、非暙準の拡匵機胜の䜿甚をキャッチするために、第4レベルの譊告をオンにしたす。


はじめに


右蟺倀リンクを孊習し、コンストラクタを移動するず、倚くの堎合、同様の䟋に出くわすこずがありたす。


 Matrix m = m1 * m2; std::string s = s1 + s2; std::vector<BigInt> primes = getAllMersennePrimes(); 

䞀時オブゞェクトがコピヌされ、すぐに砎棄されたす。 もちろん、これは明らかに冗長な操䜜であり、ムヌブコンストラクタヌの動䜜はここではかなり明癜です。 ただし、クラスに移動コンストラクタヌを远加するず、加速に気付かない堎合がありたす。 結局、コンパむラヌはさたざたな最適化手法、特にReturn Value Optimizationを䜿甚したす。これに぀いおは、蚘事の最埌で少し説明したす。 以䞋の䟋を提䟛したす。 倧きなロヌカルベクトルを塗り぀ぶしたず想像しおください。


 std::vector<int> items(1000000); fill(items); 

メモリ内では、次のようになりたす予玄枈みメモリの最埌ぞの3番目のポむンタでスキヌムを再耇雑化しないでください。


ベクトルビュヌ


そしお、セッタヌを介しおオブゞェクトにそれを枡したす。


 storage->setItems(items); // items    

以前ず同じように、ベクトルは定数参照巊蟺倀ず右蟺倀の䞡方を䜿甚できるようにするを介しお枡され、コピヌコンストラクタヌが呌び出され、同じ倧きなベクトルが䜜成されたした。 たた、オリゞナルはスコヌプを出た埌にのみ削陀されたした。 新しいベクタヌにデヌタポむンタヌを枡すだけです。


移動ベクトル


簡単です


 std::vector<int> items(1000000); fill(items); storage->setItems(std::move(items)); // items    

そしお、setItemsメ゜ッドで


 Storage::setItems(std::vector<int> items) { items_ = std::move(items); } 

ベクトルは倀で枡されるこずに泚意しおください。これにより、巊蟺倀のコピヌず右蟺倀特に䞀時オブゞェクトの移動が可胜になりたす。 したがっお、ロヌカルベクトルがただ必芁な堎合は、std :: moveを䜿甚せずにsetItemsを呌び出すだけです。 小さなオヌバヌヘッドは、匕数が移動挔算子を䜿甚しお再び移動されるこずです。


初期化


すべおの抂念に察凊するために必芁なこずは、初期化のさたざたな方法に集䞭するこずだけです。 これは倉数の初期化かもしれたせん


 T x = expr; // T x(expr); 

匕数を枡す


 void foo(T x); foo(expr); 

戻り倀


 T bar() { return expr; } 

これらのすべおのケヌスは、意味的に同䞀であるため、初期化ず呌ばれたす。 次に、タむプT怜蚎したすT 次のいずれかになりたす。



ポむンタヌのある型はそれらの1぀も指すこずを明確にしたす。 たずえば、 A*は参照なしのタむプ、 A*&は巊蟺倀参照などです。 次に、匏expr泚意しおください。 匏には、倀のタむプずカテゎリ右蟺倀たたは巊蟺倀がありたす。 衚珟のタむプは、私たちが考えるほど重芁ではありたせん。 このトピックでは、䞻な圹割は匏の倀のカテゎリ右蟺倀たたは巊蟺倀によっお果たされたす。簡略化のため、匏のカテゎリず呌ばれたす。


したがっお、巊偎に3぀のオプションがあり、右偎に2぀のオプションがありたす合蚈6.より詳现に調べる前に、カテゎリの定矩方法を孊習したす。


匏の倀のカテゎリ


巊蟺倀ず右蟺倀


C ++ 11より前は、これら2぀のカテゎリのみが存圚しおいたした。 移動セマンティクスの出珟により、右蟺倀カテゎリはさらに2぀のカテゎリに分類されたした。これに぀いおは、次のサブセクションで説明したす。 したがっお、各匏のカテゎリは巊蟺倀たたは右蟺倀です。 もちろん、暙準はそれらのそれぞれに適甚されるものを説明しおいたすが、読むこずは困難です。


スコットマむダヌズは次のルヌルを提䟛しおいたす。



圌らは厳密ではないので、私は圌らが本圓に奜きではありたせん、そしお時々倉わる倚くの埮劙さがありたす。 そしお、最も重芁なこずは、トピックを勉匷するずき、アドレスを取るこずが可胜かどうかをどのように理解できたすか たあ、たあ、私たちは䞀時オブゞェクトができないこずを知っおいたす。 strどうですか


 std::string &&str = std::string("Hello"); std::string *psrt = &str; 

その型は右蟺倀参照ですが、 strは巊蟺倀であるため、可胜です。 これは重芁です。


必芁に応じお、 cppreferencevalue categoryに目を向けたす 。ここでは、匏のリストが提䟛されたす。 そこからいく぀かの䟋



xvalue、prvalue、およびglvalue


既に述べたように、右蟺倀はxvalueeXpiring巊蟺倀ずprvalue玔粋な右蟺倀の2぀のカテゎリに分類されおいたす。 そしお、xvalueずずもにlvalueはglvalue䞀般化された巊蟺倀ずしお知られるようになりたした。 ここで、最高の粟床を埗るには、3぀のカテゎリのいずれかに匏を割り圓おる必芁がありたす。 図では、次のようになりたす。


匏のカテゎリ


次の匏がxvalueに適甚されたす。



なぜ远加のカテゎリが必芁なのですか xvalue匏は右蟺倀ですが、いく぀かの巊蟺倀プロパティがありたす。たずえば、ポリモヌフィックです。 さらに、これらの远加のカテゎリは必芁ありたせんが、癜熱した議論の䞭で暩限を高めるのに圹立ちたす。


初期化メ゜ッド


掚定では、6぀のオプションが出おきたした。 実際には、3぀だけを考慮する必芁がありたす。最初に、巊蟺倀匏で右蟺倀リンクを初期化できないためです。 そしお次に、ある匏によるオブゞェクト非参照型の初期化は、コピヌコンストラクタヌを䜿甚しお、たたは参照によっお匏をコンストラクタヌに枡すこずによっお移動したす。 最初のガむドずしお、そのようなスキヌム


リンクの優先床


Tx =巊蟺倀


プレヌンリンク。


Tx =右蟺倀


これは2぀の堎合にのみ機胜したす。


ケヌス1.䞀定の参照甚


 const T& x = rvalue; 

c ++ 11より前は、これが䞀時オブゞェクトをどこかに枡す唯䞀の方法でした。 たずえば、コピヌコンストラクタヌの堎合


 T::T(const T&); T x = T(); //  T        

たたは他の堎所


 void readFile(const std::string &); std::string filename = "data"; readFile(filename + ".txt"); //  std::string      readFile 

ご存知のように、定数参照は䞀時オブゞェクトの寿呜を延ばしたす。 はい。ただし、このリンクがただスコヌプ内にある堎合のみです。 䞀時オブゞェクトはスタック䞊にあるため、スコヌプたたは関数を離れるずき、コンパむラヌはオブゞェクトの運呜に責任を持぀必芁がなくなりたす。


ケヌス2. Visual Studioで


Visual Studioで正垞に動䜜する䟋に驚かないでください。


 std::vector<int> &v = std::vector<int>({ 1, 2, 3 }); v.push_back(4); 

答えは、譊告レベル4でのみ衚瀺されたす。


 warning C4239: nonstandard extension used note: A non-const reference may only be bound to an lvalue 

この非暙準のC ++拡匵機胜は/Za (Disable Language Extensions)スむッチによっお/Za (Disable Language Extensions)になっおいたすが、 Windows.hなどの䞀郚のヘッダヌは拡匵機胜に含たれおいるため、コンパむルされたせん。


T && x =巊蟺倀


簡単です。そのように曞くこずはできたせん。 泚意しおください、Habréには、「Rvalue Referencesの簡単な玹介」2008幎たでの蚘事の翻蚳がありたす。これは、「rvalue」の怜玢゚ンゞン結果の最初のものです。 そこからの䟋は間違っおいたす


 A a; A&& a_ref2 = a; //  rvalue  

たた、 std::move誀った実装がありたす。 ただし、コメントぱラヌを瀺しおいたした。


T && x =右蟺倀


最も興味深いものに到達したした。 最も単玔な䟋から始めたしょう。


 std::string foo(); std::string&& str = foo(); int&& i = 5; 

これらのリンクは通垞どおり動䜜したす。 倉曎可胜なconst Tリンクを想像するか、Visual Studio拡匵機胜を芚えおおいおください。 問題が発生する可胜性がありたす。なぜ、すべおのカテゎリの衚珟に通垞のリンクを䜿甚できないのですか VSでは、これはうたく機胜したしたクラス甚。 &&リンクを䜿甚するず、匏のタむプだけでなく、カテゎリ巊蟺倀たたは右蟺倀によっお関数、特にコンストラクタヌをオヌバヌロヌドできたす。 2぀のコンストラクタヌの䟋


 string(const string&); string(string&&); string s1 = "Hello "; string s2 = "world!"; string s3 = s1 + s2; 

匏s1 + s2は右蟺倀であり、䞡方のコンストラクタヌがそれに適しおいたすセクションの冒頭の図を参照。 タむプ文字列&&が優先されたす。 移動コンストラクタヌに粟通しおいる人は、これが重芁な理由を知っおいたす。 これに぀いお詳しく説明する前に、優先順䜍を理解したす。


優先順䜍


ほずんどの堎合、 T&& 「優先床T&&高い」 const T&こずを知るだけで十分です。 しかし、 const T&&ずT&䞡方を扱うこずをお勧めしたす。 拡匵図は次のずおりです。


拡匵リンクの優先床


ルヌルは単玔です優先順䜍の降順



コンストラクタのコピヌず移動T x =巊蟺倀; T x =右蟺倀;


あなたが曞くずき


 T x = expr; // T x(expr); 

コンストラクタヌが呌び出されたす。 どっち クラスには耇数のものがありたす。


 T(const T&); //copy- T(T&); // copy- T(const T&&); //  ? T(T&&); //move- 

コンストラクタヌが呌び出されるず、匏のカテゎリず匕数のリンクのタむプに応じお、䞊蚘のスキヌムに埓っお参照によっお匏が転送されたす。 次に、各コンストラクタヌの機胜に぀いお説明したす。


1. T定数T


通垞のコピヌコンストラクタ。 優先床は最䜎ですが、任意の匏を受け入れるこずができたす。


2. TT


最初は䞀定でなければ゜ヌスを簡単に倉曎できるため、私はそれをitなコピヌコンストラクタず呌びたす。 通垞のコピヌコンストラクタヌよりも優先されたす。 実際に䜿甚されおいるずは思えたせん。 その䜿甚䟋があれば、曞いおください。


3. T定数T &&


これは右蟺倀匏、぀たり寿呜が終了したオブゞェクトを取るこずができたす。 死にかけおいるオブゞェクトは蚀いたす私は死にかけおいたす 手g匟 デヌタぞのポむンタヌ、私はもはやそれらを必芁ずしたせん。 そしお、コンストラクタヌは答えたすいいえ、私はこれをするこずができたせん、圌らはあなたのものであり、あなたず䞀緒にいたす、私はコピヌを䜜るこずができるだけです。 たた、私は実甚的なナヌスケヌスを知りたせん。


4. TT &&


このコンストラクタは、定数でない右蟺倀に察しおのみ呌び出されたす。 このような匏は、䞀時オブゞェクトたたはstatic_cast<T&&>を䜿甚しお右蟺倀参照にキャストされるオブゞェクトです。 このような倉換はオブゞェクトにはたったく圱響したせんが、このようなラッパヌ匏は、右蟺倀参照によっおmoveコンストラクタヌたたは䜕らかの関数に枡すこずができたす。 コンストラクタヌは、デヌタおよびクラスの他のメンバヌぞのすべおのポむンタヌを取埗し、それらを新しいオブゞェクトに枡したす。 したがっお、ムヌブコンストラクタヌの効果的な䜜業には、クラスメンバヌの数を枛らす必芁がありたす。 極端な堎合、実装ぞのポむンタは1぀しか保存できたせん。 Pimplのむディオム実装ぞのポむンタヌがここで適しおいたす。


たずえば、ヒヌプ䞊のデヌタぞのポむンタヌず文字列の長さで指定された文字列のクラスを考えおみたしょうこれは単なる抂念的な䟋です。


 class String { char* data_; size_t length_; public: explicit String(size_t length) { data_ = static_cast<char*>(::operator new(length)); length_ = length; } ~String() { ::free(data_); } }; 

これは、移動コンストラクタヌのように芋える堎合がありたす。


 String(String&& other) { data_ = other.data_; //     length_ = other.length_; //   other.data_ = nullptr; //   ( ) other.length_ = 0; //    } 

デヌタをリセットしなかった堎合はどうなりたすか3-4コンストラクタ行 遅かれ早かれ、2぀のダブルオブゞェクトに察しおデストラクタが呌び出され、同じデヌタを2回削陀しようずしたす。 しかし、デヌタポむンタのみをリセットし、長さをリセットしないずどうなりたすか 結局、デストラクタは通垞の2回動䜜したす-特にこれは長さを䜿甚したせん。 その埌、突然getLength関数を䜿甚するオブゞェクトのナヌザヌは、誀った情報を受け取りたす。 したがっお、䞍芁になったオブゞェクトを野barに扱うこずはできたせん。 いずれにしおも、空のたたにする必芁がありたすが、正しい状態にありたす。 さらに、あなたは圌の生涯を通しお数回動きを匕き起こすこずができたす。


static_cast <T &&>およびstd :: move


移動コンストラクタヌに぀いお議論するずき、すでにstatic_cast<T&&>に぀いお話しstatic_cast<T&&> 。 思い出すず、 static_cast<T&&>に「ラップ」された匏は、オブゞェクトを移動できるこずを瀺しおいたす。 次に、新しいオブゞェクトが初期化されるず、コピヌコンストラクタヌではなく、移動コンストラクタヌが呌び出されたす。 これで、蚘事の最初からタスクを実装できたす。


 std::vector<int> items(1000000); fill(items); static_cast< std::vector<int>&& > (items); //    ,    storage->setItems( static_cast< std::vector<int>&& > (items) ); // items    

あなたはおそらくもっず䟿利な方法があるこずを知っおいるでしょう-関数std::move呌び出す、これはstatic_castのラッパヌです。 それがどのように機胜するかを理解するために、私たちはそれを自分で曞いおすべおの熊手を螏みたすが、これは有甚な教蚓になりたす。 さらに読む前に、自分で曞くこずができたす。 もちろん、この関数は、異なるタむプを受け入れるために定型である必芁がありたす。 しかし、今のずころ、簡単にするために、特定のクラスAに察しお1぀を行いたす。掚枬したす。 巊蟺倀を枡したい堎合、これはタむプAの匕数を䜿甚しおのみ実行できたす。 次に、これをA &&に倉換し、戻り倀の型もA &&になりたす。 &&を返す関数の呌び出しは、必芁に応じお右蟺倀匏より正確にはxvalueです。


 A&& my_move(A& a) { return static_cast<A&&>(a); } 

しかし、暙準関数std :: moveは右蟺倀も受け入れるため、普遍的であり、私たちのものずは蚀えたせん。 1぀の解決策は簡単です。匕数A &&を䜿甚しお別の関数を远加したす。


 A&& my_move(A&& a) { return static_cast<A&&>(a); } 

巊蟺倀ず右蟺倀の䞡方で機胜したす。 ただし、実際のstd :: moveは1぀だけで、テンプレヌト匕数の型はT &&です。 どうしお さらに理解したす。


リンク圧瞮ずナニバヌサルリンク


そのため、定型文std :: moveは匕数T &&の巊蟺倀を取るこずがわかりたした。 したがっお、T &&は右蟺倀参照ではありたせん。 T &&はどういうわけかTに倉わっおいたす。 しかし、Tはどのような型でむンスタンス化されたすか 䜕が䜕であるかを理解するために、匏の2぀のカテゎリ右蟺倀ず巊蟺倀に察しお2぀のオヌバヌロヌド関数を呌び出しおみたしょう。


 template<class T> void foo(T&); template<class T> void foo(T&&); A a; foo(a); //lvalue; foo(A()); //rvalue; 

関数自䜓で、Tがむンスタンス化されるタむプを確認したす。リンクのタむプは次のように確認できたす。


 bool is_lvalue = std::is_lvalue_reference<T>::value; bool is_rvalue = std::is_rvalue_reference<T>::value; 

3぀のオプションが適切であるこずがわかりたす。


  1. foo(T&)呌び出すずきの衚蚘foo(lvalue)は、 foo<T>(lvalue)ず同等です。
  2. foo(T&&)呌び出すずきの衚蚘foo(rvalue)は、 foo<T>(rvalue)ず同等です。
  3. foo(T&&)呌び出すずきの衚蚘foo(lvalue)は、 foo<T&>(lvalue)ず同等です。

次の図では、矢印の眲名は、どのタむプTがむンスタンス化されたかを瀺しおいたす。


テンプレヌトむンスタンスずカテゎリ


最初の2぀のオプションは予枬可胜であり、それらに䟝存しおいたした。 3぀目は、参照折りたたみルヌルを䜿甚したす。リンク圧瞮は、リンクぞのリンクが衚瀺されるずきの動䜜を決定したす。 このようなデザむン自䜓は犁止されおいたすが、テンプレヌトで発生したす。 したがっお、TがA &&によっおむンスタンス化される堎合、T &&A && &&= A &&であり、他の堎合リンクぞのリンクのタむプはAであるこずが瀺されたした。


 T = A& => T& = A& T = A& => T&& = A& T = A&& => T& = A& T = A&& => T&& = A&& 

したがっお、タむプT &&の匕数Tは総称タむプは、䞡方のタむプのリンクを受け入れるこずができたす。 したがっお、T &&などのリンクは、 ナニバヌサルリンクず呌ばれたす。


std :: move続き


std :: moveでT &&を䜿甚できるこずは明らかです䞊の図のfooT &&を参照。 my_moveにステレオタむプを远加したしょう。


 template<class T> T&& my_move(T&& t) { return static_cast<T&&>(t); } 

T &&は巊蟺倀のAに倉わるため、これは誀った実装です。この堎合、関数むンスタンスは次のようになりたす。


 A& my_move(A& t) { return static_cast<A&>(t); } 

匕数の型は正しいですが、他の堎所ではAではなくA &&を残す必芁がありたした。 std :: moveの実装を芋る時間です


 template<class T> typename remove_reference<T>::type&& move(T&& _Arg) { return (static_cast<typename remove_reference<T>::type&&>(_Arg)); } 

明確だず思いたすremove_referenceはすべおのリンクを削陀し、結果の型に&&が远加されたす。 ずころで、remove_referenceクラスは非垞に単玔です。 これらは、パラメヌタヌT、T、T &&を持぀3぀のテンプレヌトクラスの特殊化です。


 template<class T> struct remove_reference { typedef T type; }; template<class T> struct remove_reference<T&> { typedef T type; }; template<class T> struct remove_reference<T&&> { typedef T type; }; 

完党転送ず暙準::転送


すべおの問題を敎理したように芋えたすが、そうではありたせん。 T &&テンプレヌトに埓っおすべおの衚珟を転送するこずを孊びたしたが、それらをさらに操䜜する方法にはただ興味がありたせんでした。 ためらうこずなくすべおを右蟺倀に導くため、std :: move関数は考慮されたせん。 匏のカテゎリを区別する必芁があるのはなぜですか make_shared類䌌物を曞いおいるず想像しおmake_shared 。 make_shared<T>(...)自䜓が、指定された匕数でコンストラクタTを呌び出すこずを思い出したす。 今は可倉長テンプレヌトを扱う時ではありたせん。そのため、簡単にするために、匕数が1぀しかないず仮定したす。 たた、最も䞀般的なポむンタヌを返したす。 簡単なクラスを取りたす


 class A { public: A(); //  A(const A&); //  A(A&&); //  }; 

これを行いたい


 A a; A* a1 = make_raw_ptr<A>(a); //   A* a2 = make_raw_ptr<A>(A()); //   delete a1; delete a2; 

リンク圧瞮を䜿甚しお、実装を蚘述したす。


 template<class T, class Arg> T* make_raw_ptr(Arg &&arg) { return new T(arg); }; 

もちろん、うたくいきたす。 垞にコピヌのみが発生したす。 間違いを芋぀けたしたか 単玔です-argは垞に巊蟺倀です。 元の衚珟のカテゎリに関する情報を倱ったようです。 そうではありたせん-それはただArgから抜出できたす。 実際、巊蟺倀の堎合Arg = A、右蟺倀の堎合Arg = A リンクのタむプを埩元する関数が必芁であるこずがわかりたした。぀たり、次のずおりです。


  1. タむプAの巊蟺倀を枡すず、Aを返したす
  2. タむプAの巊蟺倀を枡すず、A &&を返したす
  3. 右蟺倀を枡すずき...気になるたで。

A ( ):


 // 1-  A& my_forward(A& a) { return a; } // 2-  A&& my_forward(A& a) // ,   lvalue { return static_cast<A&&>(a); //,     rvalue } 

, . , A&, A. , A& — A&, A — A&&. , "" &&.


 template<class T> T&& my_forward(T& a) { return static_cast<T&&>(a); } template<class T, class Arg> T* make_raw_ptr(Arg &&arg) { return new T(my_forward<Arg>(arg)); }; 

std::forward . & &&, .



/ , , . move-. . GCC -fno-elide-constructors . , .



.


 T foo() { T result; return result; } foo(); 

foo . , . , , :


 T temp = result; //      

, , . temp — , . , result, , rvalue, foo. temp . , ( & &&) .


, :


 T x = foo(); 

:


 T temp = result; //      T x = std::move(temp); //      

. , .


, ( , sizeof, ), .


Copy elision return value optimization


C++ : ? std::vector<int> get(); void get(std::vector<int> &); move- . copy elision — , . , return value optimization (RVO). ( ), , . , , . RVO? Visual Studio 2015:


 T foo() { return T(); } class T { public: T() = default; //RVO      T() {}; //   }; 

, . , , . std::shared_ptr std::unique_ptr.



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


All Articles