• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
C++ lecture-1
 

C++ lecture-1

on

  • 323 views

 

Statistics

Views

Total Views
323
Views on SlideShare
323
Embed Views
0

Actions

Likes
0
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    C++ lecture-1 C++ lecture-1 Presentation Transcript

    • C++ 言語講習会第 1 回資料 書いた人:@sunaemon0(回路屋 2 年)
    • 1 コンストラクタ 前回最後に使ったコードを再掲します。 list 1 constructor0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <cstdio > class hal_adc { private : unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } }; int main(int , char **) { hal_adc adc0; adc. set_value (3); std :: printf ("%dn", adc. get_value ()); return 0; }
    • さて、c のコードの関数内で、 1 2 3 4 5 6 struct a { int i; }; struct a b; みたいなことを書いたとします。 6 行目の意味は、スタック領域に構造体 a 分の領域を確保す るということです。
    • 1 2 3 4 5 6 struct a { int i; }; struct a b = {1}; と書けばこれは、スタック領域に構造体 a 分の領域を確保し その中の i を 1 で初期化するという意味になります。
    • list 2 constructor1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # include <cstdio > class hal_adc { private : unsigned int value ; public : hal_adc () { printf (" constructor called n"); } }; int main(int , char **) { hal_adc adc0; // constructor called return 0; } このプログラムを実行してみればわかりますが、14 行目が 実行されると、hal adc::hal adc が呼び出されます。 この呼び出される関数をコンストラクタと呼びます。
    • つまり、C++ で 1 hal_adc adc0; と書けば、スタック領域に hal adc 分の領域を確保し、 hal adc のコンストラクタにこの領域を初期化させるという 意味になります。
    • 2 デフォルトコンストラクタ 最初のコードでは、コンストラクタを定義しませんでした が、コンパイルが通ります。これは、明示的にコンストラク タを書かなかった場合、コンパイラがデフォルトコンストラ クタを暗黙的に生成するためです。 デフォルトコンストラクタとは引数を持たないコンストラク タ、または全ての引数にデフォルト引数が設定されているコ ンストラクタのことを言います。 コンパイル生成のデフォルトコンストラクタはこの場合何も しない関数になります。 この仕様によって C との上位互換性が保たれています。 コンストラクタは引数を持つことができます。この場合は記
    • 法が少し変わります。 list 3 constructor2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # include <cstdio > class hal_adc { unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } hal_adc ( unsigned int value) { this -> value = value; } hal_adc () { this -> value = value; } }; int main(int , char **) { hal_adc adc (3); std :: printf ("%dn", adc. get_value ()); return 0; }
    • ユーザー定義のコンストラクタがあるためデフォルトコンス トラクタは生成されないことに注意してください。 list 4 constructor3.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include <cstdio > class hal_adc { unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } hal_adc ( unsigned int value) { this -> value = value; } }; int main(int , char **) { hal_adc adc; // no matching function for call to ‘ hal_adc :: hal_adc () ’ std :: printf ("%dn", adc. get_value ()); return 0; }
    • C では以下のように定義と初期化を同時に行なっても、分け て行なってもコストは変わりませんでした。 1 struct a b = {1}; 1 2 struct a b; b. value = 1; しかし、C++ においては、一般的にはそうではありません。 定義と初期化を同時に行った場合、コンストラクタが一回呼 ばれるだけです。 c++ で後者のように書いた場合は、定義と初期化を行った後 に、更に代入を行うという意味になります。
    • またコンストラクタには以下のように初期化リストを指定で きます。 基本的には、メンバ変数全てについて初期化子を指定し ます。 list 5 constructor4.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # include <cstdio > struct data { data () { std :: printf (" default constructor called n"); } data(int) { std :: printf ("2nd constructor called n"); } }; struct hal_adc { data dat; hal_adc (int d) : dat(d) {}; hal_adc () : dat () {}; }; int main(int , char **) { hal_adc adc0; // default constructor called hal_adc adc1 (1); // 2nd constructor called }
    • 先ほどのように、初期化子を省略した場合、デフォルトコン ストラクタが呼ばれます。もし初期化子を省略されたメンバ 変数が、デフォルトコンストラクタを持たない場合、コンパ イルエラーが起きます。 const なメンバ変数及び参照は初期化子によって初期化され なければそれ以降変更することは出来ません。
    • 初期化子の呼ばれる順序は初期化リストの順序にではなく、 メンバの宣言された順番であることに気をつけてください。 混乱を避けるため、初期化リストの順序はメンバの宣言され た順序にあわせてください。
    • list 6 constructor5.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <cstdio > struct data { data(int dat) { std :: printf ("%d ", dat); } }; struct hal_adc { data dat0; data dat1; hal_adc () : dat0 (1) , dat1 (2) {}; hal_adc (int a) : dat1 (2) , dat0 (1) {}; }; int main(int , char **) { hal_adc adc0; // 1 2 std :: printf ("n"); hal_adc adc1 (1); // 1 2 std :: printf ("n"); return 0; } また、new 演算子でインスタンスを作る場合もコンストラク タが呼ばれます。
    • 別にコンストラクタを定義しない場合に、暗黙的にデフォル トコンストラクタが作られてほしくないい場合、=delete を 後につけます。 list 7 constructor6.cpp 1 2 3 4 5 6 7 8 9 10 11 # include <cstdio > struct hal_adc { hal_adc () = delete ; }; int main(int argc , char ** argv) { hal_adc adc0; // use of deleted function return 0; } デフォルトコンストラクタを明示的に private で宣言すると いう方法もありますが、こちらのほうが意図がわかりやすい のでおすすめします。
    • 別にコンストラクタを定義する場合に、デフォルトコンスト ラクタをコンパイラ生成させたい場合、=defalut を後につけ ます。 list 8 constructor7.cpp 1 2 3 4 5 6 7 8 9 10 11 12 # include <cstdio > struct hal_adc { hal_adc () = default ; hal_adc (int a); }; int main(int argc , char ** argv) { hal_adc adc0; // ok return 0; } ここでは詳しく述べませんが、コンパイラが生成したデフォ ルトコンストラクタと、それに等価な明示的に書いたデフォ
    • ルトコンストラクタは C++ の文法上扱いが異なるためこの ような措置があります。
    • 3 コピーコンストラクタ 引数として自分自身のインスタンスを取るコンストラクタの ことをコピーコンストラクタと呼びます。 正確には、class T のコンストラクタであって、引数として &T, const &T, volatile &T, volatile const &T 型の変数を取る ものをいいます。 参照であることに注意してください。 コピーコンストラクタはその名の通りインスタンスをコピー して新しいコンストラクタを作る必要があるときに呼び出さ れます。
    • list 9 copyconstructor0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # include <cstdio > struct hal_adc { hal_adc () = default ; hal_adc ( const hal_adc &) { std :: printf ("copy constructor called n"); } }; int main(int , char **) { hal_adc adc0; hal_adc adc1 = adc0; // copy constructor called return 0; }
    • 先ほどと同じようにユーザー定義のコピーコンストラクタを 定義しない場合、コンパイラはにコピーコンストラクタを生 成します。 仕様上インスタンスをコピー出来ないようにしたいクラスで はコピーコンストラクタを削除することを忘れないようにし てください。
    • 引数を値渡しする場合にもコピーコンストラクタが呼ばれ ます。 list 10 copyconstructor1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # include <cstdio > struct hal_adc { hal_adc () = default ; hal_adc ( const hal_adc &) { std :: printf ("copy constructor called n"); } }; void f( const hal_adc &) { } void g( hal_adc ) { } int main(int , char **) { hal_adc adc; f(adc); // no constructor called g(adc); // constructor not called return 0; }
    • 4 デストラクタ スコープを外れたり、delete されたりしたインスタンスは、 そのデストラクタが呼ばれ解体されます。 list 11 destructor0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # include <cstdio > struct hal_adc { hal_adc () { std :: printf (" default constructor called n"); } hal_adc ( const hal_adc &) { std :: printf ("copy constructor called n"); } ˜ hal_adc () { std :: printf (" destructor called n"); } }; int main(int , char **) { hal_adc adc; // constructor called return 0; // destructor called }
    • 静的領域に確保されたインスタンスは、main 関数が呼ばれ る前にコンストラクタで構築され、main を抜けた後にデス トラクタによって解体されます。 デストラクタそのものはそんなに難しくはないのですが、例 外や継承と組み合わさると注意が必要になってきます。
    • 5 RAII コンストラクタで new を使って領域を確保し、デストラクタ で delete で領域を開放することで delete 忘れを防止でき ます。 このようにコンストラクタで資源を確保し、デストラクタで それを開放するというパターンを Resource Acquisition is Initialization(RAII) と言います。 例外安全なプログラムを書くにはこの概念が不可欠です。
    • list 12 vector0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # include <cstdio > # include <vector > class raii_test { char *buf; public : rail_test ( raii_test &x) = delete ; operator =() = delete ; raii_test (int size) { this ->buf = new char[size ]; } ˜ rail_test () { delete [] this ->buf; } } int main(int , char **) { raii_test a(10); // new std :: scanf ("%s", a); std :: printf ("%s", a); } // delete
    • 標準ライブラリでは、 std::vector,std::unique ptr,shd::shared ptr(メモリ資源を RAII で管理) や std::fstream(ファイルポインタを RAII で管理) や std::lock guard(セマフォと RAII で管理) などが例として 挙げられます。
    • 6 const list 13 hal0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include <stdio .h> struct hal_adc { unsigned int value ; }; void set_adc_value ( hal_adc *adc , unsigned int value ) { adc -> value = value ; } unsigned int get_adc_value (const hal_adc *adc) { return adc -> value ; } int main(int argc , char ** argv) { hal_adc adc; set_adc_value (&adc , 3); printf ("%dn", get_adc_value (& adc)); return 0; }
    • list 14 hal1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include <cstdio > class hal_adc { private : unsigned int value ; public : unsigned int get_value () { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } }; int main(int argc , char ** argv) { hal_adc adc; adc. set_value (3); std :: printf ("%dn", adc. get_value ()); return 0; } この2つを比較してみると、get adc value には const とい うキーワードがあるのに対し、hal adc::get value には
    • const というキーワードがありません。 list 15 hal2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # include <stdio .h> struct hal_adc { unsigned int value ; }; void set_adc_value ( hal_adc *adc , unsigned int value ) { adc -> value = value ; } unsigned int get_adc_value (const hal_adc *adc) { return adc -> value ; } int main(int argc , char ** argv) { const hal_adc adc = {1}; printf ("%dn", get_adc_value (& adc)); return 0; } 上のプログラムの 17 行目のようなことをするのには、下の
    • ように書く必要があります。 list 16 hal3.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # include <cstdio > class hal_adc { private : unsigned int value ; public : hal_adc (int vlaue ) : value (vlaue ) { } unsigned int get_value () const { return this -> value; } void set_value ( unsigned int value) { this -> value = value; } }; int main(int argc , char ** argv) { const hal_adc adc; std :: printf ("%dn", adc. get_value ()); return 0; } このように関数名のあとに const を書くと this ポインタの中
    • 身を変更できなくなります。 また、const のインスタンスの非 const なメンバ関数は呼べ ません。
    • 7 演算子オーバーロード 例えば次のような複素数を実装したクラスを考えてみます。 list 17 operator0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class complex { double re , im; public : complex ( double re , double im) { this ->re = re; this ->im = im; } complex add( complex &z) { this ->re += z.re; this ->im += z.im; return *this; } }; int main(int , char **) { complex x(1.0 , 2.1); complex y(1.0 , 2.3); x.add(y); // returns {2.0 , 4.4} }
    • こういう実装もありえるでしょう。 list 18 operator1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class complex { double re , im; public : complex ( double this ->re = this ->im = } friend complex }; re , double im) { re; im; add( complex &x, complex &z); complex add( complex &x, complex &y) { return complex (x.re + y.re , x.im + y.im); // calles constructor } int main(int , char **) { complex x(1.0 , 2.1); complex y(1.0 , 2.3); add(x,y); // returns {2.0 , 4.4} } でも使い勝手が悪いです。
    • 特に複数の数字を足すときに見た目が煩雑になります。 そこで、C++ では add(x,y) や x.add(y) の代わりに x+y とかけ るようにする仕組みがあります。これを演算子オーバーロー ドと言います。 具体的には以下のように書きます。
    • list 19 operator2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class complex { double re , im; public : complex ( double re , double im) { this ->re = re; this ->im = im; } complex operator +( complex &z) { this ->re += z.re; this ->im += z.im; return *this; } }; int main(int , char **) { complex x(1.0 , 2.1); complex y(1.0 , 2.3); x+y; // returns {2.0 , 4.4} }
    • list 20 operator3.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class complex { double re , im; public : complex ( double this ->re = this ->im = } friend complex }; re , double im) { re; im; operator +( complex &x, complex &z); complex operator +( complex &x, complex &y) { return complex (x.re + y.re , x.im + y.im); // calles constructor } int main(int , char **) { complex x(1.0 , 2.1); complex y(1.0 , 2.3); x+y; // returns {2.0 , 4.4} } C++ では演算子をオーバーロードすることはできますが、新 しい演算子を定義したり、結合性を変えたり、優先順位を変
    • えたりすることはできません。
    • C++ ではほとんどの単項演算子と、二項演算子がオーバー ロードできます。オーバーロード出来ないのは三項演算子?: とスコープ解決子::くらいです。 ただし、++,–,(),[],-> もオーバーロードできますが、後述の 通り、オーバーロード方法が少し他とは異なります。 普通の演算子については、もしクラス T で、ある単項演算子 @がオーバーロードされていれば、@ x というのは、 operator@(x) ないし、x.operator@() と等価になります。 また、もしクラス T で、ある単項演算子@がオーバーロード されていれば、x @ y というのは、operator@(x, y) ないし、 x.operator@(y) と等価になります。
    • ++ 及び–は前置・後置の区別があり、普通引数を変更するの で扱いが少し異なりますがここでは省略します。 =,(),[],-> は非メンバ関数としてオーバーロードすることはで きません。 operator=は、代入演算子とよばれます。他の演算子は継承 されるのに対し、代入演算子は継承されません。また引数の 型が T, T&, const T&, volatile T&, or const volatile T&のうち どれかである者は特にコピー代入演算子と呼ばれます。 operator() は、他と違っていくつの引数でも取れます。 operator->() は特殊ですが細かいことは省略します。
    • 8 代入演算子 代入演算子はすでに初期化されている変数にインスタンスを 代入した時に呼ばれます。 list 21 assignment0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # include <cstdio > struct hal_adc { hal_adc () { std :: printf (" default constructor called n"); } hal_adc ( const hal_adc &) { std :: printf ("copy constructor called n"); } hal_adc & operator =( const hal_adc &x) { std :: printf ("copy assignment operator called n"); return *this; } }; int main(int , char **) { hal_adc adc0; // calles default constructor
    • 19 20 21 22 hal_adc adc1 = adc0; // calles copy constructor adc0 = adc1; // calles assignment operator return 0; }
    • 9 iostream operator¡¡のオーバーロードを乱用して、C++ ではこんなこ とができるようになりました。オーバーロードによって %d みたいに型を明示しなくても標準出力に吐き出せるようにな りました。 list 22 iostream0.cpp 1 2 3 4 5 6 7 8 9 10 # include <string > # include <iostream > int main(int ,char **) { std :: string s; std :: cin >> s; std :: cout << s << std :: endl; std :: cerr << "no error << std :: endl" }
    • 10 関数オブジェクト operator() は先述したように引数の個数が特に決まっていま せん。 list 23 function0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > int succ0 (int i) { return i+1; } struct succ1 { int operator () (int i) { return i+1; } }; int main(int , char **) { int (*f0)(int) = succ0; succ1 f1; std :: cout << f0 (1) << std :: endl; std :: cout << f1 (1) << std :: endl; }
    • このように operator() を使うとまるでまるでそのクラスのイ ンスタンスが関数のように振る舞います。 このように関数のように振る舞うオブジェクトを関数オブ ジェクトといいます。
    • 11 型推論 以下のように auto というキーワードを使うことで右辺から 自動的に型が推論され、変数定義を楽に書けるようになり ます。 1 2 3 4 auto x = 5; const auto *v = &x, u = 6; static auto y = 0.0; auto int r; // // // // OK: x has type OK: v has type OK: y has type error : auto is int const int*, u has type const int double not a storage -class - specifier Working Draft, Standard for Programming Language C++(N3337) p149 より引用
    • 12 テンプレート 本格的には次回以降やりますが、C++ にはテンプレートとい う便利な奴があります。 マクロを型安全にしたものであると言えます。
    • list 24 template0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > # include <string > using namespace std; # define add(T) T add_ ##T(T x, T y){ return x+y;} add(int); add( double ); add( string ); int main(int , char **) { cout << add_int (1, 2) << endl; // int version cout << add_double (1.0 , 2.0) << endl; // double version cout << add_string ("a", "b") << endl; // string version }
    • list 25 template1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # include <iostream > # include <string > using namespace std; template < class T> T add(T x, T y) { return x+y; } template int add <int >(int ,int); template double add <double >( double , double ); template double add <string >( string , string ); int main(int , char **) { cout << add <int >(1 , 2) << endl; // int version cout << add <double >(1.0 , 2.0) << endl; // double version cout << add <string >("a", "b") << endl; // string version }
    • list 26 template2.cpp 1 2 3 4 5 6 7 8 9 10 11 12 # include <iostream > # include <complex > template < class T> T add(T x, T y) { return x+y; } int main(int , char **) { std :: cout << add (1, 2) << std :: endl; // 版 int std :: cout << add (1.0 , 2.0) << std :: endl; // 版 double }
    • 13 関数オブジェクトと関数ポインタ C++ には、その後に () をつけて呼び出せるものが三種類あり ます。 • 関数ポインタ • 関数オブジェクト • メンバ関数ポインタ (今回の講習会ではやらないかも)
    • 関数ポインタと関数オブジェクトについてはどちらも () を付 けるだけで同じように呼び出すことができます。 しかし、変数に保存しようとしたり、戻り値として返そうと したときには別の型になってしまいます。 list 27 function0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > int succ0 (int i) { return i+1; } struct succ1 { int operator () (int i) { return i+1; } }; int main(int , char **) { int (*f0)(int) = succ0; succ1 f1; std :: cout << f0 (1) << std :: endl; std :: cout << f1 (1) << std :: endl; }
    • そこで関数の型として、同じ物を同じ型に封じ込められるよ うにした便利なものとして、std::function があります。 list 28 function1.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # include <iostream > # include <functional > int succ0 (int i) { return i+1; } struct succ1 { int operator () (int i) { return i+1; } }; int main(int , char **) { std :: function <int(int)> f0 = succ0; std :: function <int(int)> f1 = succ1 (); std :: cout << f0 (1) << std :: endl; std :: cout << f1 (1) << std :: endl; }
    • 14 ラムダ式 さて次のようなことがしたいとします。 list 29 addsth0.cpp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # include <iostream > class add_sth { int x; public : add_sth (int _x) : {x=_x ;} int operator () (int y) const { return x+y; } }; int main(int , char **) { int x = 5; auto f = add_sth (x); std :: cout << f(10) << std :: endl; // 15 }
    • これをもっと簡単に書く方法として、ラムダ式があります。 list 30 addsth1.cpp 1 2 3 4 5 6 7 8 9 # include <iostream > int main(int , char **) { int x = 5; auto f = [x]( int y){ return x+y; }; std :: cout << f(10) << std :: endl; // 15 } 見慣れない式がありますが、これがラムダ式です。 ラムダ式の文法を解説します。BNF で書くと以下のとおり です。
    • ⟨lambda-expression⟩ ::= ⟨lambda-introducer ⟩ [⟨lambda-parameter-declaration⟩] ⟨compound-statement ⟩ ⟨lambda-introducer ⟩ ::= ‘[’ [⟨lambda-capture ⟩] ‘]’ ⟨lambda-capture ⟩ ::= ⟨capture-default ⟩ | ⟨capture-list ⟩ | ⟨capture-default ⟩ ‘,’ ⟨capture-list ⟩ ⟨capture-default ⟩ ::= ‘&’| ‘=’ ⟨capture-list ⟩ ::= ⟨capture ⟩ ‘|’ ⟨capture-list ⟩ , ⟨capture ⟩ ⟨capture ⟩ ::= ⟨identifier ⟩ | ‘&’ identifier | ‘this’ ⟨lambda-parameter-declaration⟩ ::= ‘(’ [⟨lambda-parameter-declaration-list ⟩] ‘)’ [⟨mutable-specification⟩] [⟨exception-specification⟩] [⟨lambda-return-type-clause ⟩] ⟨lambda-parameter-declaration-list ⟩ ::= ⟨lambda-parameter ⟩ | ⟨lambda-parameter ⟩ ‘,’ ⟨lambda-parameter-declaration-list ⟩ ⟨lambda-parameter ⟩ ::= ⟨decl-specifier-seq⟩ ⟨declarator ⟩
    • ⟨lambda-return-type-clause ⟩ ::= ‘->’ ⟨type-id ⟩ Working Draft, Standard for Programming Language C++(N3337) p88 を基に作成
    • 最初の [] で囲われている部分が、lambda-introducer です。 ここにキャプチャしたい変数名を書きます。 最初の例では、コンストラクタで与えられた x をコピーして 保存していますがそれに当たります。 &を変数の前に付けないと、コピーによってキャプチャしま す。キャプチャ元の変数が解体されたりしても問題ありま せん。 &を変数の前につけるとコピーして保存するのでなく、参照 によってキャプチャするようになります。参照元が変更され るとそれに従います。 [] 内のリストの先頭に [=] や [=, x] のように=を書いた場合は ラムダ式内で使用した変数が暗黙的にコピーでキャプチャさ
    • れます。 [] 内のリストの先頭に [&] や [&, x] のように&を書いた場合は ラムダ式内で使用した変数が暗黙的に参照でキャプチャされ ます。 デフォルトではラムダ式の operator() は const で宣言されま す。mutable をつけた場合、const が外れます。 次の () で囲われている部分が、 lambda-parameter-declaration です。普通の関数と同じよう にパラメータを書きます。引数なしの関数オブジェクトを書 く場合はここを省略できます。 最後の {} で囲われている部分は compound-statement で、普 通の関数と書くべきことは同じです。
    • さてこのように書くと、どう表示されるでしょうか。 1 2 3 4 5 6 7 8 9 10 11 12 int a = 1, b = 1, c = 1; auto m1 = [a, &b, &c]() mutable { auto m2 = [a, b, &c]() mutable { std :: cout << a << b << c; a = 4; b = 4; c = 4; }; a = 3; b = 3; c = 3; m2 (); }; a = 2; b = 2; c = 2; m1 (); std :: cout << a << b << c; Working Draft, Standard for Programming Language C++(N3337) p91 より引用
    • 15 演習問題 • 次のような設計でオブジェクティブにコーティングしてみ よう! – main 関数は標準入力から文字列を受け取って一文字つ づ、オブジェクト A に投げる。 – オブジェクト A は一文字つづ渡されるデータを集約す る。改行が送られてくるまではデータをプールしておい て、改行が来たらまとめて文字列としてオブジェクト B に投げる。 – オブジェクト B は投げられた文字列を単純に表示する。 • std::vector と std::shared ptr の仕様を各自調べて、 practice::vector と practice::shared ptr を作ってみよう!
    • 16 Reference http://www.cplusplus.com/ http://en.cppreference.com/w/ http://www.openstd.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
    • 17 Lisence The text of this document is distributed under the Creative Commons Attribution-ShareAlike 2.0 License.