ことのおこり
cocos2d-xをやるにあたってc++を5年振りくらいに触ったらc++11ってのになっていて、
色々知らない言葉が出てきてナニコレうわーんってなったので
なにこれワードと参考にしたリンクとざっくりしたメモを並べておく。
メモね。動作確認とかしてないです。
結局書きかけ。。。
間違ってたら教えて下さい。
なにこれ…
- rvalue reference
- std::move
- std::forward
- NRVO/RVO
- emplace (mapとかvectorとか)
- std::piecewise_construct
- std::forward_as_tuple
- lambda
意外と少ないかも
参照関連
rvalue reference
とりあえず、lvalueとrvalueというものがあって、rvalueは一時的なオブジェクトのこと。
以前から &
を使うことで参照を作ることはできたけど、それはlvalue reference、つまり
左辺値参照でしかなかった。
もし、参照が右辺値であること、つまりもう使わないよってことをを明示できたら、
わざわざ値をコピーしなくても良いってことを明示できるので、
中身をポインタの挿げ替えとか出来て、パフォーマンス向上できるよね。
ということで、&&
を使うことで明示的に右辺値を参照することができるようになった。
これによって、例えば
class A {
private:
char *p;
public:
A() {
this->p = new char[ //...
}
A(A&& r)
{
// ここでrはもう使われないことがわかっているので、
// r内部のpはコピーしないで良い
this->p = r.p;
r.p = nullptr;
}
}
とか効率的なコンストラクタを提供したりできる。
moveコンストラクタと言うらしい。
それから通常のコピーコンストラクタをprivateにしてしまえば、
コストの掛かるコピーをしようとしていることをコンパイラに
指摘させることができるんじゃないかな、多分。
std::move
rvalue -> lvalueはできないが、 lvalue -> rvalueは可能。
キャストすることができる。キャストすることで、オブジェクトをもう使わないと
明示することができる。
それを便利にしてくれるのが std::move
ということだ。
上記の例だと、
A a;
A aa(std::move(a));
とすることで、A(A&& r)
のmoveコンストラクタが呼び出される(多分)。
- 本の虫:rvalue reference 完全解説
- C++0x 右辺値参照 std::move - Faith and Brave - C++で遊ぼう
- C++0x の右辺値参照がこんなに難しいわけがない。 - C++でゲームプログラミング
- C++0x - 右辺値参照(Rvalue reference) / ムーブ・セマンティクス(Move semantics) - Faith and Brave - C++で遊ぼう
- 本当は怖くないムーブセマンティクス - yohhoyの日記(別館)
- C++0x 標準ライブラリ完全解説 ~ No.02 std::move, <utility> - 野良C++erの雑記帳
std::forward
&&
を使えば、もう使わないことを明示できるぜ、という話だったのだけど、
テンプレートの場合
template < typename T >
void f( T && t ) {}
int a = 3;
f(a); // lvalue
f(5); // rvalue
どっちもコンパイルが通ってしまう。
これは通らないと通らないで引数が増えたときにオーバーロードしなきゃならない
数が爆発的に増えて大変そうなのだけど、
template < typename T >
void f( T && t ) {
A(std::move(t)); //rvalueなんだからmoveしたい
}
ときに困るということだ。テンプレートの場合はrvalueなのかlvalueなのか
分からない。万一lvalueのときに内部でmoveされると、
f(A()); //問題ない
A a;
f(a);
// えっ、知らないうちにmoveされちゃってる?
ということが起きかねない。
そこでstd::forward
ですよ。
template < typename T >
void f( T && t ) {
A(std::forward<T>(t));
}
std::forward
は、このときTがlvalueならlvalue, rvalueならrvalue
として渡してくれる。
NRVO/RVO
(Named )Return Value Optimization
だそうだ。
コンパイラが不要なコンストラクタ呼び出し部分を省いてくれるそうな。
- C++0x における NRVO - 野良C++erの雑記帳
- 暗黙のmoveとNRVO - joynote break;
- NRVO(RVO)とMove Semantics - 神様なんて信じない僕らのために
emplace
vectorにemplace_back
というメソッドが…
mapにもemplaceが。
これは、vectorとかmapへの追加のときに、無駄にコピーが発生していたよね、ということで。
struct A {
A(char, int, const std::string&);
};
std::vector<A> a_vec;
a_vec.push_back(A('a', 1, "foo"));
とやっていて、これはAをつくって、それがコピーされてa_vecに入っていた。
これを、直接内部で生成することができる感じです。
emplace_backの場合はコンストラクタ引数を直接渡します。
a_vec.emplace_back('a', 1, "foo");
あとはmoveコンストラクタがあれば
a_vec.emplace_back(std::move(A{'a', 1, "foo"});
としても、コストを抑えられるのではないかと思います。
piecewise_construct
こいつに関しては、まず pairとかtupleのコンストラクタに関係が。
ここを見てみると書いてあるけど、つまり、
第1引数がstd::piecewise_construct
だったら、
第2, 第3引数に、pairのfirst, secondに入るオブジェクトの
コンストラクタ引数をtupleで渡しますよ、
という目印の模様。
こんな感じ。
forward_as_tuple
例えばemplaceするときに、
m.emplace(piecewise_construct, tuple1, tuple2);
// tuple1は tuple<A, B, C>(a, b, c)とか
でkey/valueのコンストラクタ引数を渡せるけれど、
このtupleもmoveで一発でいかないものかと。
そこでforward_as_tuple
を使うと
m.emplace(piecewise_construct,
forward_as_tuple(a, b, c),
forward_as_tuple(d, e, f));
「tupleとして(forwardして)渡す」ができて、
幸せになれる、ということかと。