2014. 3.31.
石立 喬

Visual Studio Express 2013 の新しい使い方(1)

――― コンソールアプリケーションで、vector、for_each、ラムダ式を使う ―――

 Visual Studio Express 2013 for Windows Desktopで、簡単なC++言語の学習用として、コンソールアプリケーションを使用する方法を説明する。GUI環境を利用できるWindows プログラムではなく、入力はキーボードから行い、出力も「コマンドプロンプト」ウインドウ内に表示される。プリンタへの出力や、グラフィック表示はできない。
 簡単なコンソールアプリケーションは、すでに、旧バージョンのVisual C++で説明してあり、Visual Studio Express 2013でも、ほとんど変わりはない。そこで、このシリーズでは、なるべく新しく導入された新機能を使って、理工系技術者が興味を持てるテーマを取り上げてみる。

コンソールアプリケーションを重視する理由
 Visual Studio Express 2013は、従来の言語別バージョンから、WindowsストアアプリやWebアプリ、そしてWindows Desktopアプリと、アプリケーション別のバージョンになっていて、Visual Studio Express 2013 for Windows Desktopでは、Visual Basic、C#、C++の言語を使用できる。それぞれの言語に役割を分担させており、C++では、Win32コンソールアプリケーションの開発に重点を置いているようにみられる。これは、標準C++を用いてのプログラム開発に便利で、Visual Studio Express 2013は、ISO C++11(C++14も)対応やISO C99ライブラリが強化されているので、最新のC++プログラミングの学習に、コンソールアプリケーションをもっと積極的に利用したい。

Win32コンソールアプリケーション(.NET Frameworkを使用しない)の始め方
1) Visual Studio Express 2013 for Windows Desktpoを起動する(デスクトップからアイコンをダブル・クリックするなどして)。
2) 「スタート ページ」ウインドウが開くので、画面左側中央付近の「開始」の下の「新しいプロジェクト」をクリックする。
3) 図1に示す「新しいプロジェクト」ウインドウが開くので、左の欄の「Visual C++」を選択し(「Visual C++」の下の「Win32」でも良い)、右側の欄で「Win32コンソールアプリケーション」を選択する。
4)下の方に、「名前」、「場所」、「ソリューション名」が自動的に表示されるので、これで良ければ(必要に応じて変更できる。「ソリューション名」は、「名前」と同じものが自動的に入る。)、「ソリューションのディレクトリを作成」のチェックはそのままにして、右下の「OK」をクリックする。Visual Studio 2013では、コンソールアプリケーションの場合、使用言語によらず、「ConsoleApplication<番号>」の番号が順次つけられて行くので、ここでは、使用言語Cppを頭につけて区別した。


図1 「新しいプロジェクト」ウインドウを設定したところ


5)図2に示す「Win32アプリケーションウイザード」が開くので、「現在のプロジェクト設定」が「コンソール アプリケーション」になっていることを確認して、「完了」をクリックする。


図2 次に開く「Win32アプリケーションウイザード」ウインドウ


6) Visual Studio Express 2013の開発環境ウインドウが開く。 図3は、その一部を示したもので、コードエディタ部には、スケルトンのプログラムができている。ここでは、すでに行番号が表示されているが、後述の方法で表示させたものである。


図3 コードエディタ部にできたスケルトンのプログラム


 右側のソリューション エクスプローラには、図4に示す「ソリューション’CppConsoleApplication1’」ができており、「ソース ファイル」、「ヘッダー ファイル」などのフォルダと各ファイルができている。


図4 ソリューションエクスプローラ


コードエディタに行番号を付ける方法
1)メニューから「ツール」→「オプション」を選択する。
2)図5のような「オプション」ウインドウが開くので、「テキスト エディタ」→「C/C++」と選択し、右の「表示」欄の「行番号」にチェックをつけて、「OK」をクリックする。


図5 行番号設定のための「オプション」ウインドウの一部


プログラムの作成
 「コードエディタ」部で、「int _tmain」の中の「 { 」と 「return 0;」 の間にプログラム(ソースコード)を記述する。
 図6は、任意の個数の正の整数を入力させ、その最大値と最小値を出力するプログラムである。同じ目的で、二種類のプログラムがあり、図6は、従来の分かり易いアルゴリズムの部分で、図7は、比較的新しい機能を取り入れた後半部を示す。


図6 従来の方法によるプログラムの部分



図7 新しい手法によるプログラムの部分


新しい方法の簡単な説明
 本サイトは、従来から、文法よりも理工系技術者が関心を持つ応用に重点を置き、文法の解説は他のサイトや書籍に頼ってきた。この方針は現在でも変わらないが、ここで初めて取り上げた手法を簡単に説明する。
◎vectorテンプレートクラス
 vectorは、動的配列を提供する。動的とは、予め要素数を宣言しておく必要がなく、自由に追加、削減ができることである。テンプレートなので、< >の中に色々な型を入れて使うことができる。
   vector<int> data;
で宣言しておくと、
   data.push_back(n);
で、配列dataの末尾に、新しい要素として追加できる。nはint型で、その要素への値の設定に用いられる。
 末尾の要素を削除するには、
   data.pop_back();
を用いる。
 任意の要素へのアクセスは、通常の配列と同様にdata[i]を用いても良いし、data.at(i)を使っても良いが、vectorは、そのような使い方は、ほとんどしない。多く使われるのは、下記に示すfor_each関数におけるコンテナ(入れ物、格納庫)としてである。
ヘッダには<vector>、名前空間にはstdを必要とする。
◎for_eachテンプレート関数
 forループと似ているが、最初と最後(厳密には最後の次)の要素を指定する二つのイテレータと、関数を引数とする関数である。関数の形をしているが、値は返さない。イテレータ(iterator、繰り返すもの、繰り返させるもの、反復子)は、この場合、vector<int>::iterator it;などで宣言でき、it=data.begin();などで設定する。ただし、下記のように直接、for_each関数に入れることが多い。
   for_each(data.begin(), data.end(), function);
 for_each関数は、前述のvectorと縁が深い。vectorで宣言された配列の全要素に一定の関数を適用する場合に便利である。data.begin()は先頭イテレータ(反復子)、data.end()は末尾イテレータ(反復子)として受け取る。最近では、data.begin()の代わりにbegin(data)を、data.end()の代わりにend(data)を使用しても良い。
 forループの場合は、ループ内で、continue、break等が使え、ループ内の処理を詳しく記述しやすく、便利ともいえる。しかし、スキップ等がなく、単純な処理には、for_eachの方が趣旨が明確で良いとされる。
ヘッダには<algorithm>を必要とする。
◎ラムダ式(lambda expression)
 上述のfor_each関数には、引数として関数を渡さなくてはならない。簡単な関数であれば、for_each関数の引数として、直接インラインに書いてしまうのが簡単であり、これに用いるのがラムダ式(単にラムダとも言う)である。一番簡単な構文(ラムダ式の本体のreturn文で戻り値の型が推測できる場合と戻り値が無い場合)は、
  [<キャプチャ句>](<引数リスト>){<文>; <文>;}
の形をしていて、キャプチャ句は空欄にすることも、複数個書くこともできる。キャプチャ句が空欄の時は、外部のスコープ内の変数に関係が無いことを意味する。キャプチャ句には、参照渡しは&付で、値渡しは&なしで外部のスコープ内の変数名を入れる。

プログラムについての説明
 図7の34行で、配列dataを、int型のvectorと宣言する。これによって、39行のdata.push_back(n);が使用できて、キー入力された値nを、順次配列の要素に入れて行くことができる。
 入力が終わると、41行のfor_each関数に移り、配列dataの先頭要素位置data.begin()と最終(最終+1)要素位置data.end()と、実行する関数が与えられる。
 ここで、関数は、
  [&max, &min](int i){if(i>max) max=i; if(i<min) min=i;}
のように、ラムダ式で書かれている。
 これは、外部でスコープ内の変数maxとminを参照渡しでやりとりし、配列dataの要素値を、順次変数iとして受け取り、
  if(i > max) max = i;
  if(i < min) min = i;
を実行する、との意味である。

得られた結果
 図8は、上述したプログラムを実行した結果である。上は、従来の方法によるもの、下はvector、for_each、ラムダ式を試した結果で、従来の方法と全く変わらない結果が得られた。


図8 実行結果


 結論としてどちらの方法が良いか?と聞かれると困る。少なくとも、ここで示した例では、for_eachを使ったり、ラムダ式を使ったりするメリットは感じられない。理工系のテーマで、もっと適した例がないか、これから探してみたい。
 おまけとして、従来のforループで、vectorと反復子を使った場合を図9に示す。これにより、これらへの理解が深まれば幸いである。イテレータはポインタと似ているが、異なる点もあり、it < data.end()ではなく、it != data.end()とすべきと言われているが、これで動作する。


図9 等価なforループ