Edited at

C++11について気になった事リストと参考リンクメモ

More than 1 year has passed since last update.


ことのおこり

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コンストラクタが呼び出される(多分)。


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

だそうだ。

コンパイラが不要なコンストラクタ呼び出し部分を省いてくれるそうな。


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して)渡す」ができて、

幸せになれる、ということかと。