C++のmoveの意味は本当にそんなに恐ろしくありません
5001 ワード
前言
1.move意味再考
まず,この語を文法に基づいて
1.1.意味vs.文法
もっと簡単に率直に言う.構文=あるソースコードの書き方 意味=このソースコードはどのように実行します
ここでは同じ意味に対して、2つの異なる文法が使用できます.一般的には前のものを選んでプログラムを書きますが、これは「文法糖」にすぎません.本来は後者の書き方を使えば十分であるが、読解や編纂の観点から後者の書き方が理解の難しさを増しているため、前の書き方(文法と呼ぶべき)が増えている.ここでこの例を挙げるのは、文法と意味の関係が1:1の関係ではないことを説明するためだけである.
1.2.コピーvs.移動
1.3.moveの2つの意味
先ほど述べた
上のコードでは、意味のない
以上のように
1.4.C++11のmoveの意味
ここでいう
したがって、ここでの
2.moveの意味の使用
ここでは、
2.1.C++11標準ライブラリとmove
現在の
2.2.移動後の状態?
非常に正確な答えを見つけるには、実は面倒です.
しかし、あるクラスライブラリについて、
2.3 moveの意味を利用して効率的な関数を作成する
To move or not to move: that is the question.
― Bjarne Stroustrup, 2010
C++11
の新しい特性として、move
(move semantics
)と
(rvalue reference
)がよく一緒に議論され、ネット上でも多くの分析文章が見られる.しかし、
という新しい概念を使ってmove
を解釈すると、実は敷居が高く、知っているようで分からないような気がします.少なくとも私にとってはそうです.そこで本稿では,
を用いてmove
を解釈することをできるだけ避け,実際のプログラミングにおけるその使い方を説明する.本稿では、C++
標準ライブラリの関数を例に挙げて説明します.move
に適用されるクラスライブラリをどのように自分で作成するかは、考慮の範囲内ではありません(これは
に関連しています).車輪さえ使えない人が良い車輪を作ることはできないと信じています.1.move意味再考
まず,この語を文法に基づいて
move
と
に分割して考え,まず
を説明し,次にmove
を説明する.1.1.意味vs.文法
もっと簡単に率直に言う.
b = a[4];
//
b = *(a + 4);
ここでは同じ意味に対して、2つの異なる文法が使用できます.一般的には前のものを選んでプログラムを書きますが、これは「文法糖」にすぎません.本来は後者の書き方を使えば十分であるが、読解や編纂の観点から後者の書き方が理解の難しさを増しているため、前の書き方(文法と呼ぶべき)が増えている.ここでこの例を挙げるのは、文法と意味の関係が1:1の関係ではないことを説明するためだけである.
1.2.コピーvs.移動
move
に対してcopy
である.周知のように、C++
の変数はすべて
である.いわゆる
の変数は、その動作がint
タイプと同じであり、典型的には、パラメータとして関数に入力されるには、形式パラメータとしてコピーする必要があり、その変化は元の変数に影響を与えない.ここでは,先ほど述べた文法/意味の関係に戻り,u=t
という文法に対して,実際の意味は t
の内容を u
にコピーすることである.1.3.moveの2つの意味
先ほど述べた
の実装は,本質的に複製に依存する.しかし、配列内の要素を2
に乗じると、値の意味だけでこのような関数が実現され、結果は次のようになります.std::vector twice_vector(std::vector); // 2
std::vector v = { /* */ };
std::vector w = twice_vector( v );
// v
上のコードでは、意味のない
copy
が何度も発生し、時間を失ってメモリが失われました.もちろん、ここでは「引用タイプを使いましょう.std::vector&
を知っていれば、ここでも同様に使用できます.move
はまた、スマートポインタmove
のような別の場所を使用します.ただし、unique_ptr
によって所有権の移動を実現することができ、以下のコードを参照してください.boost::unique_ptr p( new int(42) );
boost::unique_ptr q = p; // !( )
boost::unique_ptr q ← p; // move( '←' move , )
assert(*q==42 && p.get()==NULL);
以上のように
には2つの意味がある--1極めて低い性能消費で複製を実現する;②所有権の移転1.4.C++11のmoveの意味
ここでいう
move
は、上記のmove
の2つの意味の後者であり、コードに訳すと、int *p = new int(42); // p 42
int *q;
// 42 p q
q = p;
p = NULL;
assert(*q==42 && p==NULL);
したがって、ここでの
move
は、move
の値をmove
にコピーし、
の値を
に付与することである.こんなに簡単なことである以上、なぜ
を新しい特性としなければならないのだろうか.理由としては、①プログラマー所有権の移転が徹底していないこと(後のNULL
)を避けること、②C++11
という意味をより明確にすること、③文法サポート(つまり右値参照)を増やしてコンパイラがコンパイル時に最適化できるようにすること、などが挙げられる.2.moveの意味の使用
ここでは、
p=NULL
の標準ライブラリを例として説明する.2.1.C++11標準ライブラリとmove
現在の
move
標準ライブラリは基本的にC++11
のサポートを追加しています.上記のC++11
だけでなく、move
の実際のstd::unique_ptr
とC++03
の2つの私たちが最もよく知っているクラスライブラリもサポートされています.std::string s1 = "apple";
std::string s2 = "banana";
s1 = std::move(s2); // s2 s1, s2
// s1=="banana"
std::vector v1;
std::vector v2 = {1, 2, 3, 4};
v1 = std::move(v2); // v2 v1
// v1=={1, 2, 3, 4}
std::string s1 = "apple";
std::string s2 = "banana";
std::vector<:string> dict;
dict.push_back( s1 ); // s1
// s1 == "apple"
dict.push_back( std::move(s2) ); // s2
// ( s2 )
std::vector<:string> v;
v = std::move(dict); //
// v[0]=="apple" && v[1]=="banana"
2.2.移動後の状態?
非常に正確な答えを見つけるには、実は面倒です.
std::string
の標準クラスライブラリの言い方は「依然として有効だが、状態は不明」であり、実際には一般的には空であるが保証されていない.std::vector
を例に挙げて説明する.std::string t = "xmas", u;
u = std::move(t);
// OK: t = "X"; ,
// OK: t.size() ; ,
// NG: t[2]; ,
しかし、あるクラスライブラリについて、
C++11
が所有権の移動という意味を表し、極めて低いパフォーマンス消費でレプリケーションを実現するのではなく(参照string
の2つの意味)、このクラスライブラリは、転送後の状態が空であることを保証しなければならないか、またはmove
を例に挙げなければならない.std::unique_ptr p1( int new(42) );
std::unique_ptr p2;
p2 = std::move(p1);
// p1 , p1.get()==NULL
2.3 moveの意味を利用して効率的な関数を作成する
1.3
の最初に述べた問題に戻り、配列内の要素をunique_ptr
に乗算する関数実装を記述する.次に,1.3
により効率的に実現できる.typedef std::vector IntVec;
IntVec twice_vector(IntVec a)
{
for (auto& e : a)
e *= 2;
return std::move(a);
}
IntVec v = { /*...*/ };
IntVec w = twice_vector( std::move(v) );