SlideShare una empresa de Scribd logo
1 de 34
Descargar para leer sin conexión
MOVE SEMANTICS
MITSUTAKA TAKEDA
MITSUTAKA.TAKEDA@GMAIL.COM
TABLE OF CONTENTS
1. C++ for Java/Scala
1.1. Copy Semantics(Value Semantics)
1.2. Copy Semantics(Value Semantics)
1.3. 寄り道(Reference/Pointer Semantics)
1.4. Where is My Copy?
1.5. Copyの現実
2. Move Semantics
2.1. Move!
2.2. 寄り道(リソースの所有権)
3. L/R Value
4. L/R Value Reference
4.1. 注意点(L/R Value と L/R Value Reference)
5. Non Copyiable Object(Movable Object)
6. Move Semantics導入前後
6.1. Move Semantics導入前
6.2. Move Semantics導入後
1 C++ FOR JAVA/SCALA
1.1 COPY SEMANTICS(VALUE SEMANTICS)
C++ではオブジェクトは基本的に組み込み型のように振舞う(Value
Semantics)。 Javaで言うところのValue Object(int, String, etc)。
int x = 0;
int y = x;
y = 10; // xの値は 0 か 10のどちらでしょうか?
1.2 COPY SEMANTICS(VALUE SEMANTICS)
コピーとは等価の別オブジェクト(コピー先への変更はコピー元に
影響しない)を作ること。
int x = 0;
int y = x; // xをyへコピー
assert(x == y); // コピー直後はxとyは等価。
y = 10; // yを変更。
assert(x == 0); // y(コピー先)への変更はx(コピー元)へ影響しない。
assert(y == 10);
1.3 寄り道(REFERENCE/POINTER SEMANTICS)
Reference/Pointerセマンティックスも利用するこができます。
int main() {
int x = 0;
int& y = x; // yはxへの参照。
y = 10;
assert(x == y);
assert(x == 10);
assert(y == 10);
int* z = &x; // zはxへのポインタ。
*z = 20; // ポインタの指す先(x)を変更。
assert(x == y); // yはxへの参照なのでxが変更されればyも変わる。
assert(x == 20);
assert(y == 20);
assert(*z == 20);
return 0;
}
1.4 WHERE IS MY COPY?
何回コピーするでしょう?
class MyClass {
public:
MyClass() = default;
MyClass(const MyClass&) {
std::cout << "I'm Copied!n";
}
};
MyClass f(MyClass p0,
const MyClass& p1) {
return p0;
}
int main() {
MyClass m;
MyClass n = f(m, m);
return 0;
}
1.5 COPYの現実
compilerが最適化してくれる場合もあり。
Return Value Optimization(Named Return Value Optimization)
MyClass f() {
return MyClass();
}
void f_optimized(MyClass* out){
return *out;// Return Valueが出力用パラメータに置き換えられる。
}
MyClass ret = f();// この呼出しは以下の呼出しに置き換えられてコピーが発生しない。
MyClass ret;
f_optimized(&ret);// 関数内では、関数の呼出元が用意したメモリ領域(ret)を直接扱う。
2 MOVE SEMANTICS
特定の状況ではオブジェクトのコピーは不要では?例えば、2度使
わないオブジェクト(関数の戻り値等)
X func(){
X x;
// xに対して何かする。
return x;
}
// funcの呼び出し側。
X ret = func();// retにfuncの戻り値をコピーする。コピー必要か?
2.1 MOVE!
コピー不要・不可能なケースではオブジェクトの内部リソース(メモ
リ等)の所有権を移譲(move)すれば良い!
struct X{
BigData* p; // Excelで処理しなければいけない程のビックデータ(リソース)へのポインタ。
};
// コピー(Javaで言うところのclone)のケースではBigDataをコピーする必要がある。
X original = ...;
X copy = original; // originalのBigDataをコピー。時間が掛る。
predictFuture(copy);// コピーへの操作はoriginalへ影響しない。
// retを操作して影響を受けるoriginalはいない!funcの戻り値をretへコピーする必要なし!
X ret = func(); // BigDataをコピーするのではなく、ポインタ(32/64bit)を戻り値からretへコピーするだけで良い。
predictFuture(ret);
2.2 寄り道(リソースの所有権)
C++のようにGCがない言語ではリソースの所有権という概念が非
常に重要。
どのオブジェクトがどのリソースを所有しているか(リソースの後片
付けを誰が行うのか)を厳密に表現する必要。 Java/Scalaではリソ
ースへの参照型Tのメンバ変数。C++では以下の5種類に分類。
C++ 型 所有権とコメント
std::unique_ptr<T> 1つのオブジェクトがリソースを専有
std::shared_ptr<T> 複数のオブジェクトがリソースを共有
raw pointer (T*) 所有はせずただのオプショナルなリソースへ
の参照
reference (T&) 所有はせず必須な参照
value (T) 1つのオブジェクトがリソースを専有。リソース
は動的多態不要
面倒(ダングリング参照&循環参照でのメモリ・リーク)だが、モデル
の意味を厳密に表現 & プログラムの性能を精確に予測可能。
Scala -> C++の移植はツライ。。。主語・目的語が省略された日本
語を英語に翻訳するみたいな感じ。
3 L/R VALUE
l(L)value(左辺値)&r(R)value(右辺値)
lvalueとはメモリ上に実体がありアドレスを取得できるオブジェク
ト。rvalueはアドレス取得不可能なもの。 名前の由来は代入演算
子(=)の左辺になれるものがlvalue。右辺にしかなれないものが
rvalue。
void f(int a){
std::cout << &a << std::endl; // aはlvalue。
}
int g(int a) {
return a;
}
int main() {
int b = 0;
std::cout << &b << std::endl; // bはlvalue。
f(b);
std::cout << &0 << std::endl; // リテラルはrvalue。コンパイル・エラー。
std::cout << &(g(b)) << std::endl; // 関数の戻り値はrvalue。コンパイル・エラー。
return 0;
}
4 L/R VALUE REFERENCE
L/R Valueへの参照。
int main() {
int a = 0; // lvalue 'a'
int& la = a; // lvalue reference to lvalue 'a'
int&& ra = a; // rvalue reference to lvalueは不可能。コンパイル・エラー。
int& lx = 0; // lvalue reference to rvalueは不可能。コンパイル・エラー。
int&& rx = 0; // rvalue reference to rvalue。
const int& cla = a; // const lvalue reference to lvalue。
const int&& cra = a; // const rvalue reference to lvalue。コンパイル・エラー。
const int& clx = 0; // const lvalue reference to rvalue。
const int&& crx = 0; // const rvalue reference to rvalue。
return 0;
}
4.1 注意点(L/R VALUE と L/R VALUE REFERENCE)
L/R Valueは値のカテゴリ(型のことじゃないよ!)、L/R Value
Referenceは型。 L/R Valueと L/R Value Referenceは直交した概
念。
int f(){
return 0;
}
int main() {
int a = 0; // aはint型でlvalueカテゴリ。
int& l = a; // lはintへのlvalue reference型でlvalueカテゴリ。
int&& r = f(); // rはintへのrvalue reference型でlvalueカテゴリ。
int&& rr = r; // コンパイル・エラー。rはlvalueなのでrvalue referenceへは束縛できない。
return 0;
}
5 NON COPYIABLE OBJECT(MOVABLE OBJECT)
本質的にコピーできないオブジェクトがある。例えばthreadオブジ
ェクトを"コピーする"とはどういう意味? コピーした時点から2つのス
レッドが実行される?
void task(){}
int main() {
std::thread t(task);
std::thread copy = t;// スレッドをコピーするとはどういう意味?
return 0;
}
6 MOVE SEMANTICS導入前後
コピーが高価なリソースを保持するオブジェクト & Non Copyiable
Objectの扱いは非常に煩雑。
6.1 MOVE SEMANTICS導入前
不定な一時変数やら、手続型カッコ悪い。
// Move Semantics導入前
struct BigData {
void* peta_byte_data; // ガチで大きなリソース。
};
// DBからビッグ・データを複数読み込む。
void readFromDB(std::vector<BigData>& ret){ }
void predictFuture(std::vector<BigData>& d) {}
int main() {
std::vector<BigData> data; // コピーを防ぐため、一時変数を定義。
readFromDB(data); // 一時変数の領域へDBから読み込んだデータを書き込む。
predictFuture(data);
return 0;
}
6.2 MOVE SEMANTICS導入後
性能を犠牲にせずより関数型的に記述できる。
struct BigData {
void* peta_byte_data; // ガチで大きなリソース。
};
std::vector<BigData> readFromDB(){ }// 手続から関数へ。
void predictFuture(std::vector<BigData> d) {}
int main() {
predictFuture(readFromDB());// 一時変数不要に。
return 0;
}
6.3 MOVE SEMANTICSの注意点
moveの恩恵を受けることができるのは、moveがcopyと比較して
安価なときだけ。
例えばBigDataのようなデータ構造では、copyはPeta Byteコピー
しなければいけないのに対して、 moveはポインタのコピー
(32/64bit)だけなのでmoveはcopyと比較して非常に安価。
一方、標準のarrayはスタック上にメモリが確保されることが義務
付けられているため、 データのコピーは不可避。
struct BigData {
void* peta_byte_data; // ガチで大きなリソース。
};
template<typename base_t, typename expo_t>
constexpr base_t POW(base_t base, expo_t expo) {
return (expo != 0 )? base * POW(base, expo -1) : 1;
}
constexpr auto Peta = POW(10l,15l);// Peta = 10^15
BigData f() {
BigData x; // fのスタック上に配置されるのはポインタ(peta_byte_data)のみ。
return x; // 関数fの終了後ポインタは破棄されるがポインタが指す先の領域は生き残る。
}
std::array<int, Peta> g() {
std::array<int, Peta> y; // Peta Byteのデータがgのスタック上に確保される。
return y; // yのデータは関数gの終了とともに破棄される。
}
int main() {
BigData good = f(); // moveの恩恵を受けることができる。
std::array<int, Peta> bad = g(); // moveの恩恵は受けることができない。(コピーされる)
return 0;
}
6.4 MOVE FOR USER-DEFINED TYPE
gcc/clangでは、ユーザ定義型がmove semanticsをサポートできる
とき、 ユーザ定義型は自動的にMovableな型(Move Constructor
& Move Assignment Operatorが定義された型)にな る。しかし
Visual Studioでは自分でユーザ定義型をMovableにする必要が
あり。VSのおかげでmoveが実装できるようになりました(感謝!)
void acquire(void*) {} // リソースの取得。
void release(void*) {} // リソースの開放。
struct BigData {
void* peta_byte_data; // Resource
// RAII (Resource Accquisition Is Initialization)
BigData(){ acquire(peta_byte_data); }
~BigData(){ release(peta_byte_data); }
// Move Semantics
BigData(BigData&& o){ // Move Constructor
peta_byte_data = o.peta_byte_data; // ポインタのコピー。
o.peta_byte_data = nullptr; // move元のポインタをnullに設定してリソースの2重開放を防止。
}
BigData& operator=(BigData&& o){ // Move Assignment Operator
if(this != &o) { // 自己代入禁止。
release(peta_byte_data); // 自分のリソースを開放。
peta_byte_data = o.peta_byte_data; // ポインタのコピー。
o.peta_byte_data = nullptr; // move元のポインタをnullに設定してリソースの2重開放を防止。
}
return *this;
}
};
6.5 COPY VS. MOVE
オブジェクトはいつcopy(Copy Constructor/Assignemt Operator
が呼ばれる)されて、 いつmove(Move Constructor/Assignemt
Operatorが呼ばれる)されるの?
通常の関数のoverload解決ルールと一緒。bindの可否と優先順
位。結論、rvalueはmoveされる。
  Value    
  R V L V(non const型) L V (const 型)
R V Reference できる(優先) できない できない
L V Reference できない できる(優先) できない
const L V Reference できる できる できる
struct MyClass {
MyClass() {}
// Copy Semantics
MyClass(const MyClass& o){ std::cout << "Copy Constructor" << std::endl; }
MyClass& operator=(const MyClass& o) { std::cout << "Copy Assignment Operator" << std::endl; return *this
// Move Semantics
MyClass(MyClass&& o){ std::cout << "Move Constructor" << std::endl; }
MyClass& operator=(MyClass&& o) { std::cout << "Move Assignment Operator" << std::endl; return *this; }
};
void f( MyClass&& x) { std::cout << "R V Reference" << std::endl; }
void f( MyClass& x) { std::cout << "L V Reference" << std::endl;}
void f(const MyClass& x) { std::cout << "const L V Reference" << std::endl;}
int main() {
f(MyClass()); // R V Reference
MyClass x; // xはnon const型のL V。
f(x); // L V Reference
const MyClass y; // yはconst型のL V。
f(y); // const L V Reference
MyClass a;
MyClass b = a; // Copy Constructor
MyClass c;
c = a; // Copy Assignment Operator
MyClass d = std::move(a); // Move Constructor。std::moveは引数へのをrvalue referenceを取得する。後述。
MyClass e;
e = std::move(b); // Move Assignment Operator
return 0;
}
6.6 COPY VS. MOVE (その2)
void f(MyClass&& x){ // xはL Value? R Value?
MyClass y = x; // Copy or Moved?
}
6.7 MOVE SEMANTICSの実装について
例外安全のために実際にMove Semanticsを実装するときは
noexceptにできるか熟考しましょう。
例外安全についてここに記すには余白が小さすぎるので、また別
の機会に。
7 STD::MOVE & STD::FORWARD
std::moveはlvalueをrvalueに変換する。
std::forwardは条件付でlvalueをrvalueに変換する。
struct MyClass{};
void f(MyClass&& x){
MyClass y = std::move(x); // xはrvalue referenceなのでxにはR Valueが束縛されているためmoveは安全。
}
void f(const MyClass& x) {
MyClass y = std::move(x); // xはlvalue referenceなのでxにはL Valueが束縛されているためmoveは危険。
}
7.1 TYPE DEDUCTION
関数テンプレートは実引数からその引数の型を推論することがで
きる。
template <typename T>
void f(T x) {
std::cout << typeid(x).name() << std::endl;
assert(typeid(x) == typeid(int));
}
int main() {
int a = 0;
f(a); // Tはintと推論される。
const int b = 0;
f(b); // Tはconst intと推論される。
int& l = a;
f(l); // Tはintと推論される。
int&& r = 0;
f(r); // Tはintと推論される。
return 0;
}
7.2 FORWARDING(UNIVERSAL) REFERENCE
template parameterにreferenceやconstが付属すると、推論され
た型Tとオブジェクトxの型は違う。
Forwarding Referenceを使用すると、R Valueが関数テンプレート
に渡されたときTはそのR Valueの型に、 L Valueが渡されたときL
Value Referenceに推論される。
template <typename T> struct TD; // コンパイラの型推論の結果を表示するテクニック。
template <typename T>
void f(T&& x){ // Forwarding(Universal) Reference。
TD<T> a; // メモ: コンパイル・エラーで型が表示される。
// error: implicit instantiation of undefined template 'TD<int &>'
// TD<decltype(x)> a;
}
int main() {
int a = 0;
f(a); // Tはint&と推論される。xの型はint&。(LVR)
const int b = 0;
f(b); // Tはconst int&と推論される。xの型はconst int&。(const LVR)
int& l = a;
f(l); // Tはint&と推論される。xの型はint&。(LVR)
int&& r = 0;
f(r); // Tはint&と推論される。xの型はint&。(LVR)
f(int(0)); // Tはintと推論される。xの型はint&&。(RVR)
return 0;
}
7.3 FORWARDING REFERENCE & STD::FORAWRD
型推論の結果を考慮して右辺値が渡されたときはmoveで、左辺
値が渡されたときはcopyするためにstd::forwardテンプレートが 使
用できる。
template <typename T>
void f(T&& x){
// std::forwardの宣言。
// template< class T >
// T&& forward( typename std::remove_reference<T>::type& t );
// Tがnon reference(例えばint)のときは、std::forrwardはR Value Referenceに。
// TがL value reference(例えばint&)のときは、std::forrwardはL Value Referenceに。
T tmp = std::forward<T>(x);
}
8 キーワード
lvalue & rvalue
lvalue reference & rvalue reference
forwarding reference
move semantics (Movable)
copy semantics (Copyable)
RAII
type deduction
9 参考情報
"C++ Rvalue References Explained"
"Back to the Basics! Essentials of Modern C++
Style@CPPCON2014," Herb Sutter,
"std::move@cppreference.com"
"Effective Modern C++," Scott Meyers
http://thbecker.net/articles/rvalue_references/section_01.html
https://www.youtube.com/watch?v=xnqTKD8uD64
http://en.cppreference.com/w/cpp/utility/move

Más contenido relacionado

La actualidad más candente

ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14Ryo Suzuki
 
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるときunique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるときShintarou Okada
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由kikairoya
 
The Why and How of Java8 at LINE Fukuoka
The Why and How of Java8 at LINE FukuokaThe Why and How of Java8 at LINE Fukuoka
The Why and How of Java8 at LINE FukuokaYouhei Nitta
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門natrium11321
 
C++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプC++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプKohsuke Yuasa
 
BoostAsioで可読性を求めるのは間違っているだろうか
BoostAsioで可読性を求めるのは間違っているだろうかBoostAsioで可読性を求めるのは間違っているだろうか
BoostAsioで可読性を求めるのは間違っているだろうかYuki Miyatake
 
Fork/Join Framework。そしてLambdaへ。
Fork/Join Framework。そしてLambdaへ。Fork/Join Framework。そしてLambdaへ。
Fork/Join Framework。そしてLambdaへ。Yuichi Sakuraba
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライドkoturn 0;
 
プロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswiftプロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswiftTomohiro Kumagai
 
Material
MaterialMaterial
Material_TUNE_
 
C++入門?
C++入門?C++入門?
C++入門?tsudaa
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61TATSUYA HAYAMIZU
 
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
Boost.勉強会#19東京 Effective Modern C++とC++ Core GuidelinesBoost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
Boost.勉強会#19東京 Effective Modern C++とC++ Core GuidelinesShintarou Okada
 
はてなブックマーク in Scala
はてなブックマーク in Scalaはてなブックマーク in Scala
はてなブックマーク in ScalaLintaro Ina
 
Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4Takashi Hoshino
 
ナウなヤングにバカうけのイカしたタグ付き共用体
ナウなヤングにバカうけのイカしたタグ付き共用体ナウなヤングにバカうけのイカしたタグ付き共用体
ナウなヤングにバカうけのイカしたタグ付き共用体digitalghost
 

La actualidad más candente (20)

ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるときunique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
 
More C++11
More C++11More C++11
More C++11
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
The Why and How of Java8 at LINE Fukuoka
The Why and How of Java8 at LINE FukuokaThe Why and How of Java8 at LINE Fukuoka
The Why and How of Java8 at LINE Fukuoka
 
競技プログラミングのためのC++入門
競技プログラミングのためのC++入門競技プログラミングのためのC++入門
競技プログラミングのためのC++入門
 
C++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプC++ ポインタ ブートキャンプ
C++ ポインタ ブートキャンプ
 
BoostAsioで可読性を求めるのは間違っているだろうか
BoostAsioで可読性を求めるのは間違っているだろうかBoostAsioで可読性を求めるのは間違っているだろうか
BoostAsioで可読性を求めるのは間違っているだろうか
 
Fork/Join Framework。そしてLambdaへ。
Fork/Join Framework。そしてLambdaへ。Fork/Join Framework。そしてLambdaへ。
Fork/Join Framework。そしてLambdaへ。
 
第2回勉強会スライド
第2回勉強会スライド第2回勉強会スライド
第2回勉強会スライド
 
プロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswiftプロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswift
 
Material
MaterialMaterial
Material
 
C++入門?
C++入門?C++入門?
C++入門?
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
Boost.勉強会#19東京 Effective Modern C++とC++ Core GuidelinesBoost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
Boost.勉強会#19東京 Effective Modern C++とC++ Core Guidelines
 
Sml#探検隊
Sml#探検隊Sml#探検隊
Sml#探検隊
 
Map
MapMap
Map
 
はてなブックマーク in Scala
はてなブックマーク in Scalaはてなブックマーク in Scala
はてなブックマーク in Scala
 
Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4Effective Modern C++ 勉強会#1 Item3,4
Effective Modern C++ 勉強会#1 Item3,4
 
ナウなヤングにバカうけのイカしたタグ付き共用体
ナウなヤングにバカうけのイカしたタグ付き共用体ナウなヤングにバカうけのイカしたタグ付き共用体
ナウなヤングにバカうけのイカしたタグ付き共用体
 

Similar a Move semantics

var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18
var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18
var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18Syo Ikeda
 
第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」yoshiaki iwanaga
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8y_taka_23
 
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„和弘 井之上
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターンMoriharu Ohzu
 
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftSwift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftTomohiro Kumagai
 
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswiftSwift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswiftTomohiro Kumagai
 
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】株式会社ランチェスター
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-Kazunari Hara
 
Sansan様 登壇資料
Sansan様 登壇資料Sansan様 登壇資料
Sansan様 登壇資料Daisuke Nagata
 
函数プログラミングの エッセンスと考え方
函数プログラミングのエッセンスと考え方函数プログラミングのエッセンスと考え方
函数プログラミングの エッセンスと考え方啓 小笠原
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competitionyak1ex
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11えぴ 福田
 
型プロファイラ:抽象解釈に基づくRuby 3の静的解析
型プロファイラ:抽象解釈に基づくRuby 3の静的解析型プロファイラ:抽象解釈に基づくRuby 3の静的解析
型プロファイラ:抽象解釈に基づくRuby 3の静的解析mametter
 
メタメタプログラミングRuby
メタメタプログラミングRubyメタメタプログラミングRuby
メタメタプログラミングRubyemasaka
 

Similar a Move semantics (20)

var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18
var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18
var RAC3 = ReactiveCocoa + Swift @ ReactiveCocoa Tokyo #rac_tokyo 10/18
 
第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
 
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第4回 ‟関数„
 
クロージャデザインパターン
クロージャデザインパターンクロージャデザインパターン
クロージャデザインパターン
 
Actor&stm
Actor&stmActor&stm
Actor&stm
 
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftSwift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswift
 
Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7Pfi Seminar 2010 1 7
Pfi Seminar 2010 1 7
 
Swiftおさらい
SwiftおさらいSwiftおさらい
Swiftおさらい
 
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswiftSwift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
Swift 3 を書くときに知っておきたい API デザインガイドライン #love_swift #akibaswift
 
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
 
What is Metasepi?
What is Metasepi?What is Metasepi?
What is Metasepi?
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
 
Sansan様 登壇資料
Sansan様 登壇資料Sansan様 登壇資料
Sansan様 登壇資料
 
函数プログラミングの エッセンスと考え方
函数プログラミングのエッセンスと考え方函数プログラミングのエッセンスと考え方
函数プログラミングの エッセンスと考え方
 
cp-11. ポインタ
cp-11. ポインタcp-11. ポインタ
cp-11. ポインタ
 
C++0x in programming competition
C++0x in programming competitionC++0x in programming competition
C++0x in programming competition
 
ぱっと見でわかるC++11
ぱっと見でわかるC++11ぱっと見でわかるC++11
ぱっと見でわかるC++11
 
型プロファイラ:抽象解釈に基づくRuby 3の静的解析
型プロファイラ:抽象解釈に基づくRuby 3の静的解析型プロファイラ:抽象解釈に基づくRuby 3の静的解析
型プロファイラ:抽象解釈に基づくRuby 3の静的解析
 
メタメタプログラミングRuby
メタメタプログラミングRubyメタメタプログラミングRuby
メタメタプログラミングRuby
 

Move semantics

  • 2. TABLE OF CONTENTS 1. C++ for Java/Scala 1.1. Copy Semantics(Value Semantics) 1.2. Copy Semantics(Value Semantics) 1.3. 寄り道(Reference/Pointer Semantics) 1.4. Where is My Copy? 1.5. Copyの現実 2. Move Semantics 2.1. Move! 2.2. 寄り道(リソースの所有権) 3. L/R Value 4. L/R Value Reference 4.1. 注意点(L/R Value と L/R Value Reference) 5. Non Copyiable Object(Movable Object) 6. Move Semantics導入前後 6.1. Move Semantics導入前 6.2. Move Semantics導入後
  • 3. 1 C++ FOR JAVA/SCALA
  • 4. 1.1 COPY SEMANTICS(VALUE SEMANTICS) C++ではオブジェクトは基本的に組み込み型のように振舞う(Value Semantics)。 Javaで言うところのValue Object(int, String, etc)。 int x = 0; int y = x; y = 10; // xの値は 0 か 10のどちらでしょうか?
  • 5. 1.2 COPY SEMANTICS(VALUE SEMANTICS) コピーとは等価の別オブジェクト(コピー先への変更はコピー元に 影響しない)を作ること。 int x = 0; int y = x; // xをyへコピー assert(x == y); // コピー直後はxとyは等価。 y = 10; // yを変更。 assert(x == 0); // y(コピー先)への変更はx(コピー元)へ影響しない。 assert(y == 10);
  • 6. 1.3 寄り道(REFERENCE/POINTER SEMANTICS) Reference/Pointerセマンティックスも利用するこができます。 int main() { int x = 0; int& y = x; // yはxへの参照。 y = 10; assert(x == y); assert(x == 10); assert(y == 10); int* z = &x; // zはxへのポインタ。 *z = 20; // ポインタの指す先(x)を変更。 assert(x == y); // yはxへの参照なのでxが変更されればyも変わる。 assert(x == 20); assert(y == 20); assert(*z == 20); return 0; }
  • 7. 1.4 WHERE IS MY COPY? 何回コピーするでしょう? class MyClass { public: MyClass() = default; MyClass(const MyClass&) { std::cout << "I'm Copied!n"; } }; MyClass f(MyClass p0, const MyClass& p1) { return p0; } int main() { MyClass m; MyClass n = f(m, m); return 0; }
  • 8. 1.5 COPYの現実 compilerが最適化してくれる場合もあり。 Return Value Optimization(Named Return Value Optimization) MyClass f() { return MyClass(); } void f_optimized(MyClass* out){ return *out;// Return Valueが出力用パラメータに置き換えられる。 } MyClass ret = f();// この呼出しは以下の呼出しに置き換えられてコピーが発生しない。 MyClass ret; f_optimized(&ret);// 関数内では、関数の呼出元が用意したメモリ領域(ret)を直接扱う。
  • 9. 2 MOVE SEMANTICS 特定の状況ではオブジェクトのコピーは不要では?例えば、2度使 わないオブジェクト(関数の戻り値等) X func(){ X x; // xに対して何かする。 return x; } // funcの呼び出し側。 X ret = func();// retにfuncの戻り値をコピーする。コピー必要か?
  • 10. 2.1 MOVE! コピー不要・不可能なケースではオブジェクトの内部リソース(メモ リ等)の所有権を移譲(move)すれば良い! struct X{ BigData* p; // Excelで処理しなければいけない程のビックデータ(リソース)へのポインタ。 }; // コピー(Javaで言うところのclone)のケースではBigDataをコピーする必要がある。 X original = ...; X copy = original; // originalのBigDataをコピー。時間が掛る。 predictFuture(copy);// コピーへの操作はoriginalへ影響しない。 // retを操作して影響を受けるoriginalはいない!funcの戻り値をretへコピーする必要なし! X ret = func(); // BigDataをコピーするのではなく、ポインタ(32/64bit)を戻り値からretへコピーするだけで良い。 predictFuture(ret);
  • 12. C++ 型 所有権とコメント std::unique_ptr<T> 1つのオブジェクトがリソースを専有 std::shared_ptr<T> 複数のオブジェクトがリソースを共有 raw pointer (T*) 所有はせずただのオプショナルなリソースへ の参照 reference (T&) 所有はせず必須な参照 value (T) 1つのオブジェクトがリソースを専有。リソース は動的多態不要 面倒(ダングリング参照&循環参照でのメモリ・リーク)だが、モデル の意味を厳密に表現 & プログラムの性能を精確に予測可能。 Scala -> C++の移植はツライ。。。主語・目的語が省略された日本 語を英語に翻訳するみたいな感じ。
  • 13. 3 L/R VALUE l(L)value(左辺値)&r(R)value(右辺値) lvalueとはメモリ上に実体がありアドレスを取得できるオブジェク ト。rvalueはアドレス取得不可能なもの。 名前の由来は代入演算 子(=)の左辺になれるものがlvalue。右辺にしかなれないものが rvalue。 void f(int a){ std::cout << &a << std::endl; // aはlvalue。 } int g(int a) { return a; } int main() { int b = 0; std::cout << &b << std::endl; // bはlvalue。 f(b); std::cout << &0 << std::endl; // リテラルはrvalue。コンパイル・エラー。 std::cout << &(g(b)) << std::endl; // 関数の戻り値はrvalue。コンパイル・エラー。 return 0; }
  • 14. 4 L/R VALUE REFERENCE L/R Valueへの参照。 int main() { int a = 0; // lvalue 'a' int& la = a; // lvalue reference to lvalue 'a' int&& ra = a; // rvalue reference to lvalueは不可能。コンパイル・エラー。 int& lx = 0; // lvalue reference to rvalueは不可能。コンパイル・エラー。 int&& rx = 0; // rvalue reference to rvalue。 const int& cla = a; // const lvalue reference to lvalue。 const int&& cra = a; // const rvalue reference to lvalue。コンパイル・エラー。 const int& clx = 0; // const lvalue reference to rvalue。 const int&& crx = 0; // const rvalue reference to rvalue。 return 0; }
  • 15. 4.1 注意点(L/R VALUE と L/R VALUE REFERENCE) L/R Valueは値のカテゴリ(型のことじゃないよ!)、L/R Value Referenceは型。 L/R Valueと L/R Value Referenceは直交した概 念。 int f(){ return 0; } int main() { int a = 0; // aはint型でlvalueカテゴリ。 int& l = a; // lはintへのlvalue reference型でlvalueカテゴリ。 int&& r = f(); // rはintへのrvalue reference型でlvalueカテゴリ。 int&& rr = r; // コンパイル・エラー。rはlvalueなのでrvalue referenceへは束縛できない。 return 0; }
  • 16. 5 NON COPYIABLE OBJECT(MOVABLE OBJECT) 本質的にコピーできないオブジェクトがある。例えばthreadオブジ ェクトを"コピーする"とはどういう意味? コピーした時点から2つのス レッドが実行される? void task(){} int main() { std::thread t(task); std::thread copy = t;// スレッドをコピーするとはどういう意味? return 0; }
  • 18. 6.1 MOVE SEMANTICS導入前 不定な一時変数やら、手続型カッコ悪い。 // Move Semantics導入前 struct BigData { void* peta_byte_data; // ガチで大きなリソース。 }; // DBからビッグ・データを複数読み込む。 void readFromDB(std::vector<BigData>& ret){ } void predictFuture(std::vector<BigData>& d) {} int main() { std::vector<BigData> data; // コピーを防ぐため、一時変数を定義。 readFromDB(data); // 一時変数の領域へDBから読み込んだデータを書き込む。 predictFuture(data); return 0; }
  • 19. 6.2 MOVE SEMANTICS導入後 性能を犠牲にせずより関数型的に記述できる。 struct BigData { void* peta_byte_data; // ガチで大きなリソース。 }; std::vector<BigData> readFromDB(){ }// 手続から関数へ。 void predictFuture(std::vector<BigData> d) {} int main() { predictFuture(readFromDB());// 一時変数不要に。 return 0; }
  • 20. 6.3 MOVE SEMANTICSの注意点 moveの恩恵を受けることができるのは、moveがcopyと比較して 安価なときだけ。 例えばBigDataのようなデータ構造では、copyはPeta Byteコピー しなければいけないのに対して、 moveはポインタのコピー (32/64bit)だけなのでmoveはcopyと比較して非常に安価。 一方、標準のarrayはスタック上にメモリが確保されることが義務 付けられているため、 データのコピーは不可避。
  • 21. struct BigData { void* peta_byte_data; // ガチで大きなリソース。 }; template<typename base_t, typename expo_t> constexpr base_t POW(base_t base, expo_t expo) { return (expo != 0 )? base * POW(base, expo -1) : 1; } constexpr auto Peta = POW(10l,15l);// Peta = 10^15 BigData f() { BigData x; // fのスタック上に配置されるのはポインタ(peta_byte_data)のみ。 return x; // 関数fの終了後ポインタは破棄されるがポインタが指す先の領域は生き残る。 } std::array<int, Peta> g() { std::array<int, Peta> y; // Peta Byteのデータがgのスタック上に確保される。 return y; // yのデータは関数gの終了とともに破棄される。 } int main() { BigData good = f(); // moveの恩恵を受けることができる。 std::array<int, Peta> bad = g(); // moveの恩恵は受けることができない。(コピーされる) return 0; }
  • 22. 6.4 MOVE FOR USER-DEFINED TYPE gcc/clangでは、ユーザ定義型がmove semanticsをサポートできる とき、 ユーザ定義型は自動的にMovableな型(Move Constructor & Move Assignment Operatorが定義された型)にな る。しかし Visual Studioでは自分でユーザ定義型をMovableにする必要が あり。VSのおかげでmoveが実装できるようになりました(感謝!)
  • 23. void acquire(void*) {} // リソースの取得。 void release(void*) {} // リソースの開放。 struct BigData { void* peta_byte_data; // Resource // RAII (Resource Accquisition Is Initialization) BigData(){ acquire(peta_byte_data); } ~BigData(){ release(peta_byte_data); } // Move Semantics BigData(BigData&& o){ // Move Constructor peta_byte_data = o.peta_byte_data; // ポインタのコピー。 o.peta_byte_data = nullptr; // move元のポインタをnullに設定してリソースの2重開放を防止。 } BigData& operator=(BigData&& o){ // Move Assignment Operator if(this != &o) { // 自己代入禁止。 release(peta_byte_data); // 自分のリソースを開放。 peta_byte_data = o.peta_byte_data; // ポインタのコピー。 o.peta_byte_data = nullptr; // move元のポインタをnullに設定してリソースの2重開放を防止。 } return *this; } };
  • 24. 6.5 COPY VS. MOVE オブジェクトはいつcopy(Copy Constructor/Assignemt Operator が呼ばれる)されて、 いつmove(Move Constructor/Assignemt Operatorが呼ばれる)されるの? 通常の関数のoverload解決ルールと一緒。bindの可否と優先順 位。結論、rvalueはmoveされる。   Value       R V L V(non const型) L V (const 型) R V Reference できる(優先) できない できない L V Reference できない できる(優先) できない const L V Reference できる できる できる
  • 25. struct MyClass { MyClass() {} // Copy Semantics MyClass(const MyClass& o){ std::cout << "Copy Constructor" << std::endl; } MyClass& operator=(const MyClass& o) { std::cout << "Copy Assignment Operator" << std::endl; return *this // Move Semantics MyClass(MyClass&& o){ std::cout << "Move Constructor" << std::endl; } MyClass& operator=(MyClass&& o) { std::cout << "Move Assignment Operator" << std::endl; return *this; } }; void f( MyClass&& x) { std::cout << "R V Reference" << std::endl; } void f( MyClass& x) { std::cout << "L V Reference" << std::endl;} void f(const MyClass& x) { std::cout << "const L V Reference" << std::endl;} int main() { f(MyClass()); // R V Reference MyClass x; // xはnon const型のL V。 f(x); // L V Reference const MyClass y; // yはconst型のL V。 f(y); // const L V Reference MyClass a; MyClass b = a; // Copy Constructor MyClass c; c = a; // Copy Assignment Operator MyClass d = std::move(a); // Move Constructor。std::moveは引数へのをrvalue referenceを取得する。後述。 MyClass e; e = std::move(b); // Move Assignment Operator return 0; }
  • 26. 6.6 COPY VS. MOVE (その2) void f(MyClass&& x){ // xはL Value? R Value? MyClass y = x; // Copy or Moved? }
  • 27. 6.7 MOVE SEMANTICSの実装について 例外安全のために実際にMove Semanticsを実装するときは noexceptにできるか熟考しましょう。 例外安全についてここに記すには余白が小さすぎるので、また別 の機会に。
  • 28. 7 STD::MOVE & STD::FORWARD std::moveはlvalueをrvalueに変換する。 std::forwardは条件付でlvalueをrvalueに変換する。 struct MyClass{}; void f(MyClass&& x){ MyClass y = std::move(x); // xはrvalue referenceなのでxにはR Valueが束縛されているためmoveは安全。 } void f(const MyClass& x) { MyClass y = std::move(x); // xはlvalue referenceなのでxにはL Valueが束縛されているためmoveは危険。 }
  • 29. 7.1 TYPE DEDUCTION 関数テンプレートは実引数からその引数の型を推論することがで きる。 template <typename T> void f(T x) { std::cout << typeid(x).name() << std::endl; assert(typeid(x) == typeid(int)); } int main() { int a = 0; f(a); // Tはintと推論される。 const int b = 0; f(b); // Tはconst intと推論される。 int& l = a; f(l); // Tはintと推論される。 int&& r = 0; f(r); // Tはintと推論される。 return 0; }
  • 30. 7.2 FORWARDING(UNIVERSAL) REFERENCE template parameterにreferenceやconstが付属すると、推論され た型Tとオブジェクトxの型は違う。 Forwarding Referenceを使用すると、R Valueが関数テンプレート に渡されたときTはそのR Valueの型に、 L Valueが渡されたときL Value Referenceに推論される。
  • 31. template <typename T> struct TD; // コンパイラの型推論の結果を表示するテクニック。 template <typename T> void f(T&& x){ // Forwarding(Universal) Reference。 TD<T> a; // メモ: コンパイル・エラーで型が表示される。 // error: implicit instantiation of undefined template 'TD<int &>' // TD<decltype(x)> a; } int main() { int a = 0; f(a); // Tはint&と推論される。xの型はint&。(LVR) const int b = 0; f(b); // Tはconst int&と推論される。xの型はconst int&。(const LVR) int& l = a; f(l); // Tはint&と推論される。xの型はint&。(LVR) int&& r = 0; f(r); // Tはint&と推論される。xの型はint&。(LVR) f(int(0)); // Tはintと推論される。xの型はint&&。(RVR) return 0; }
  • 32. 7.3 FORWARDING REFERENCE & STD::FORAWRD 型推論の結果を考慮して右辺値が渡されたときはmoveで、左辺 値が渡されたときはcopyするためにstd::forwardテンプレートが 使 用できる。 template <typename T> void f(T&& x){ // std::forwardの宣言。 // template< class T > // T&& forward( typename std::remove_reference<T>::type& t ); // Tがnon reference(例えばint)のときは、std::forrwardはR Value Referenceに。 // TがL value reference(例えばint&)のときは、std::forrwardはL Value Referenceに。 T tmp = std::forward<T>(x); }
  • 33. 8 キーワード lvalue & rvalue lvalue reference & rvalue reference forwarding reference move semantics (Movable) copy semantics (Copyable) RAII type deduction
  • 34. 9 参考情報 "C++ Rvalue References Explained" "Back to the Basics! Essentials of Modern C++ Style@CPPCON2014," Herb Sutter, "std::move@cppreference.com" "Effective Modern C++," Scott Meyers http://thbecker.net/articles/rvalue_references/section_01.html https://www.youtube.com/watch?v=xnqTKD8uD64 http://en.cppreference.com/w/cpp/utility/move