Visual C++

Visual Studio 2010 での C++ と MFC の新機能の詳細

Sumit Kumar

Visual Studio 2010 は、C++ の開発者に大きなメリットをもたらします。Windows 7 で提供される新機能を利用する機能から、大規模コード ベースで作業するために強化された生産性機能まで、あらゆる C++ 開発者向けに新機能が導入され、機能強化が施されています。

今回の記事では、C++ の開発者がこれまでに直面した一般的な問題のいくつかを、マイクロソフトがどのように解決したかについて説明します。具体的には、Visual Studio 2010 では、近々リリースされる C++0x 標準の主要言語機能を追加し、新しい言語機能を使用できるように標準ライブラリを見直すことによって、より新しいプログラミング モデルを使用できるようになっています。並列プログラムの作成を簡素化する並列プログラミング用の新しいライブラリとツールがあります。また、大規模コード ベースに合わせて拡張できる IntelliSense とコードの認識機能のおかげで、全体的なパフォーマンスと開発者の生産性が向上します。開発者は、デザイン時、ビルド時、コンパイル時、およびリンク時すべてに、ライブラリやその他の機能のパフォーマンス向上の恩恵を受けることになります。

Visual Studio 2010 ではビルド システムが MSBuild に移行されているため、カスタマイズが容易になり、複数バージョンへの対応がネイティブにサポートされるようになります。また、MFC ライブラリの強化により、新しい Windows 7 の API 機能を活用できるため、優れた Windows 7 アプリケーションを作成することができます。

Visual Studio 2010 に盛り込まれ、C++ に重点を置いたこれらの機能強化について詳しく見ていきましょう。

C++0x の主要言語機能

C++ の次期標準は、少しずつ完成に近づいています。C++0x の拡張機能を使用する際に役立つように、Visual Studio 2010 の Visual C++ コンパイラでは、C++0x の 6 つの主要言語機能として、ラムダ式、auto キーワード、rvalue references (rvalue 参照)、static_assert、nullptr、および decltype を有効にしています。

ラムダ式は、名前のない関数オブジェクトを暗黙に定義して構築します。ラムダ式では自然な簡易構文が実現されるため、パフォーマンスのオーバーヘッドを生じることなく、その場所で関数オブジェクトを定義できます。

関数オブジェクトは、標準テンプレート ライブラリ (STL) のアルゴリズムの動作をカスタマイズするための強力な方法で、(単純な関数とは異なり) コードとデータの両方をカプセル化できます。ただし、関数オブジェクトはクラス全体を記述する必要があるため、定義が面倒です。また、関数オブジェクトはソース コード内でもそのオブジェクトを使用する場所で定義されないため局所性がないことから使いにくく感じられます。ライブラリでは、こうした冗長性と非局所性の問題の緩和が試みられましたが、構文が複雑になること、およびコンパイル エラーがあまりわかりやすくないことから、それほど役に立っていません。また、関数オブジェクトをデータ メンバーとして定義するときインラインでは定義できないため、ライブラリから関数オブジェクトを使用してもあまり効果はありません。

これらの問題はラムダ式によって解決できます。次のコード スニペットは、プログラムでラムダ式を使用して、整数ベクトルから、x 変数と y 変数の間にある整数を取り除く処理を示しています。

v.erase(remove_if(v.begin(),
   v.end(), [x, y](int n) { 
   return x < n && n < y; }),
   v.end());

2 行目のコードがラムダ式を表しています。角かっこ (ラムダ導入子と呼びます) は、ラムダ式の定義を示します。このラムダ式は整数 n をパラメーターとして受け取り、ラムダ式が生成する関数オブジェクトはデータ メンバー x と y を保持します。これに相当する関数オブジェクトを記述した次の例と比べると、ラムダ式で実現される利便性と時間短縮の真価がわかります。

class LambdaFunctor {
public:
  LambdaFunctor(int a, int b) : m_a(a), m_b(b) { }
  bool operator()(int n) const {
    return m_a < n && n < m_b; }
private:
  int m_a;
  int m_b;
};
v.erase(remove_if(v.begin(), v.end(),
  LambdaFunctor(x, y)), v.end());

auto キーワードは最初からずっと C++ に存在していましたが、付加価値が感じられなかったため、ほとんど使用されていませんでした。C++0x では、このキーワードの用途が変更され、初期化子から変数の型を自動的に判断するようになりました。auto キーワードは、冗長性を減らし、重要なコードを目立たせるのに役立ちます。型の不一致や切り捨てエラーも回避できます。また、中間式の型を考慮しないでテンプレートを記述できるため、コードがより汎用的になり、ラムダ式のようにドキュメントに記載されない型を効率的に処理できます。次のコードは、auto キーワードを使用して、vector を反復処理する for ループ内でテンプレートの型を指定しない方法を示しています。

vector<int> v;
for (auto i = v.begin(); i != v.end(); ++i) {
// code 
}

rvalue 参照は、C++0x で導入される新しい参照型です。不要なコピーの問題を解決するのに役立ち、完全転送を可能にします。代入の右辺が rvalue のときは、左辺のオブジェクトは個別に代入を実行するのではなく、右辺のオブジェクトからリソースを奪うことができ、ムーブ セマンティクスが可能になります。

完全転送では、任意の n 個の引数を受け取り、その値を別の任意の関数に透過的に転送する、1 つの関数テンプレートを記述できます。引数の性質 (modifiable、const、lvalue または rvalueなど) は、この転送プロセス中保持されます。

template <typename T1, typename T2> void functionA(T1&& t1, T2&& t2) {
  functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}

rvalue 参照については、この記事で扱う範囲を超えているため、ここでは詳しく説明しません。詳細については、MSDN ドキュメント (msdn.microsoft.com/library/dd293668(VS.100)、英語) を参照してください。

static_assert では、実行時ではなく、コンパイル時にアサーションをテストできます。わかりやすいカスタム エラー メッセージを使用して、コンパイル エラーをトリガーすることができます。static_assert は、テンプレート パラメーターを検証する際に特に役立ちます。たとえば、次のコードをコンパイルすると、「error C2338: custom assert: n should be less than 5」というエラーが返されます。

template <int n> struct StructA {
  static_assert(n < 5, "custom assert: n should be less than 5");
};

int _tmain(int argc, _TCHAR* argv[]) {
  StructA<4> s1;
  StructA<6> s2;
  return 0;
}

nullptr は、null ポインターにタイプ セーフ性を追加し、rvalue 参照に密接に関連します。NULL マクロ (0 として定義されます) とリテラル 0 は、一般に null ポインターとして使用されます。これまでは問題になりませんでしたが、完全転送で問題が発生する可能性があるため、C++0x では NULL マクロやリテラル 0 が適切に機能するとは限りません。そのため、nullptr キーワードが導入されました。このキーワードは、特に、完全転送の関数で発生する不可解な問題を回避するために導入されています。

nullptr は、nullptr_t 型の定数です。これは、あらゆるポインター型に変換することができますが、int や char といったポインター型以外には変換できません。完全転送の関数で使用される以外にも、NULL マクロを null ポインターとして使用する場所ならどこでも nullptr を使用できます。

ただし、NULL はコンパイラで引き続きサポートされていて、まだ nullptr に置き換わっていません。これは主に、NULL が広範囲にわたって使用され、また不適切に使用されていることが多いので、既存のコードが機能しなくなることを避けるためです。しかし、将来的には、NULL を使用する場所では nullptr を使用し、下位互換性をサポートする機能として NULL を扱うことになります。

最後に、decltype では、コンパイラが任意の式に基づいて関数の戻り値の型を推論でき、完全転送がより汎用的になります。これまでの各バージョンでは、T1 と T2 の 2 つの任意の型に関して、これらの 2 つの型が使用されている式の型を推論する方法はありませんでした。decltype の機能を使用すると、たとえば、テンプレート引数を持つ式 (型 T1 および T2 を持つ sum<T1, T2>() など) を指定できます。

標準ライブラリの機能強化

C++ の標準ライブラリの大部分が書き直され、新しい C++0x の言語機能を利用してパフォーマンスを向上しています。また、新しいアルゴリズムも数多く導入されています。

標準ライブラリでは、rvalue 参照を最大限に活用してパフォーマンスを向上しています。vector や list などの型は、ムーブ コンストラクターと独自のムーブ代入演算子を持つようになりました。vector の再割り当てでは、ムーブ コンストラクターを選択することでムーブ セマンティクスが利用されるため、型にムーブ コンストラクターとムーブ代入演算子が含まれている場合は、ライブラリが自動的に選択します。

新しい C++0x の関数テンプレート make_shared<T> を使用して、オブジェクトを作成すると同時に、オブジェクトへの共有ポインターを作成できるようになりました。

auto sp = 
  make_shared<map<string,vector>>
  (args);

Visual Studio 2008 では、これと同じ機能を実現するために、次のようにコーディングする必要があります。

shared_ptr<map<string,vector>> 
  sp(new map<string,vector>(args));

make_shared<T> を使用すると、利便性 (型名を入力する回数が減ります)、堅牢性 (ポインターとオブジェクトが同時に作成されるので、従来の、名前のない shared_ptr によるリークが回避されます)、および効率性 (動的なメモリ割り当てが 2 回ではなく 1 回で行われます) が向上します。

ライブラリには、新しくてより安全なスマート ポインター型 unique_ptr が含まれるようになりました (これは、rvalue 参照で有効になっています)。その結果として、auto_ptr が廃止されました。unique_ptr が移動可能 (ただし、コピーは不可) になったことで、auto_ptr で発生していた問題が回避されます。unique_ptr では、安全性に影響を及ぼすことなく、厳密な所有権のセマンティクスを実装できます。また、rvalue 参照を認識する Visual C++ 2010 のコンテナーであれば、適切に連携できます。

コンテナーには、新しいメンバー関数 (cbegin と cend) が含まれるようになりました。これらを使用すると、コンテナーの種類に関係なく、const_iterator を使用して検査するための方法が提供されます。

vector<int> v;
 
for (auto i = v.cbegin(); i != v.cend(); ++i) {
  // i is vector<int>::const_iterator
}

Visual Studio 2010 では、C++0x に関するさまざまな論文で提案されているほとんどのアルゴリズムが標準ライブラリに追加されています。標準ライブラリでは、Dinkumware 社提供の変換ライブラリのサブセットを使用できるようになったため、UTF-8 から UTF-16 などへの変換を簡単に行えるようになりました。標準ライブラリでは、exception_ptr によって例外の伝播が可能になります。<random> ヘッダーに多くの更新が行われています。今回のリリースでは、forward_list というリンク リストが 1 つ含まれています。また、ライブラリに <system_error> ヘッダーが追加されたことで、診断が強化されました。さらに、以前のリリースで std::tr1 名前空間に存在していた TR1 機能 (shared_ptr や regex など) の多くは、std 名前空間に移動し、標準ライブラリの一部になっています。

同時実行プログラミングの機能強化

Visual Studio 2010 には、Parallel Computing Platform (並列コンピューティング プラットフォーム) が導入されています。このプラットフォームでは、捕らえにくい同時実行のバグを回避しつつ、パフォーマンスの高い並列処理コードを簡単に記述できます。これにより、従来の同時実行に関連するいくつかの問題を避けることができます。

並列コンピューティング プラットフォームは、Concurrency Runtime (ConcRT: 同時実行ランタイム)、Parallel Patterns Library (PPL: 並列パターン ライブラリ)、Asynchronous Agents Library (非同期エージェント ライブラリ)、および並列デバッグとプロファイリングの 4 つの主要部分から構成されています。

ConcRT は、最下層のソフトウェア レイヤーで、OS と対話し、複数の同時実行コンポーネントがリソースを求めて競合するのを解決します。ConcRT はユーザー モード プロセスなので、協調ブロッキングのメカニズムを使用する場合に、リソースを解放できます。ConcRT は局所性に対応し、異なるプロセッサ間の切り替えタスクを回避します。また、Windows 7 の User Mode Scheduling (UMS: ユーザー モード スケジューリング) が使用されるので、協調ブロッキングのメカニズムを使用していない場合でも、パフォーマンスを向上できます。

PPL では、並列コードを記述するためのパターンが提供されます。1 つの計算を、関数や関数オブジェクトで表すことができるより小さな計算に分解できる場合は、これらのより小さな各計算をタスクとして表すことができます。スレッドでは、ユーザーがハードウェア、OS、クリティカル セクションなどについて考慮しなければならず、問題の領域があいまいになりかねませんが、このタスクの考え方は問題領域にかなり近いものになっています。タスクは、他のタスクで実行されている処理に関係なく、同時に実行することができます。たとえば、配列の 2 つの異なる部分の並べ替えは、2 つの別のタスクによって同時に実行されます。

PPL には、並列クラス (task_handle、task_group、および structured_task_group)、並列アルゴリズム (parallel_invoke、parallel_for、および parallel_for_each)、並列コンテナー (combinable、concurrent_queue、および concurrent_vector)、ConcRT 対応の同期プリミティブ (critical_section、event、および reader_writer_lock) が含まれます。これらはすべて、タスクを最優先の概念として扱います。PPL のすべてのコンポーネントは、concurrency 名前空間にあります。

タスクのグループを使用すると、一連のタスクを実行して、それらのタスクがすべて完了するまで待機できます。そのため、並べ替えの例で言うと、配列の 2 つの部分を処理するタスクを、1 つのタスク グループにできます。これらの 2 つのタスクは、wait メンバー関数呼び出しの最後に完了することが保証されます。この例を、並列タスクとラムダ式を使用して記述した再帰形式の quicksort のコード サンプルで示します。

void quicksort(vector<int>::iterator first,
vector<int>::iterator last) {
  if (last - first < 2) { return; }
  int pivot = *first;
  auto mid1 = partition(first, last, [=](int elem) { 
    return elem < pivot; });
  auto mid2 = partition( mid1, last, [=](int elem) { 
    return elem == pivot; });
  task_group g;
  g.run([=] { quicksort(first, mid1); });
  g.run([=] { quicksort(mid2, last); });
  g.wait();
}

このコードは、parallel_invoke アルゴリズムで有効になる、構造化されたタスク グループを使用することで、さらに改良できます。parallel_invoke アルゴリズムは、2 ~ 10 個の関数オブジェクトを受け取り、ConcRT で提供される可能な限り多くのコアを使用してこれらすべてを並列実行して、処理が完了するまで待機します。

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

parallel_invoke(
  [=] { quicksort(first, mid1); },
  [=] { quicksort(mid2, last); } );

これらの各タスクで作成された、複数のより小さなタスクを使用することもできます。タスクと実行スレッド間のマッピング (およびすべてのコアが最適に使用されるようにする制御) は、ConcRT によって管理されます。したがって、計算を可能な限り多くのタスクに分解すると、すべての使用可能なコアを活用するのに役立ちます。

もう 1 つの役に立つ並列アルゴリズムは parallel_for です。これは、複数のインデックスを同時実行形式で反復処理するのに使用できます。

parallel_for(first, last, functor);
parallel_for(first, last, step, functor);

このコードでは、first から始まり last で終わる各インデックスを持つ関数オブジェクトを同時に呼び出します。

非同期エージェント ライブラリでは、データ フローに基づくプログラミング モデルが提供されます。このプログラミング モデルでは、必要なデータが使用可能かどうかによって、計算が行われるかどうかが決まります。このライブラリは、エージェント、メッセージ ブロック、およびメッセージ渡しの機能を基盤としています。エージェントはアプリケーションのコンポーネントで、特定の計算を行い、他のエージェントと非同期に通信することで、より大規模な計算の問題を解決します。エージェントどうしの通信は、メッセージ渡しの機能とメッセージ ブロックを使用して実行されます。

エージェントにはさまざまな段階を経る監視可能なライフサイクルがあります。エージェントは、PPL のタスクを使用して実行されるきめ細かな並列処理で使用するようには意図されていません。エージェントは、ConcRT のスケジューリングとリソース管理のコンポーネントに基づいて構築され、同時実行アプリケーションで共有メモリを使用した場合に発生する問題を回避するのに役立ちます。

これらのパターンを使用するために、追加のコンポーネントをリンクしたり再配布したりする必要はありません。ConcRT、PPL、および非同期エージェント ライブラリは標準ライブラリと共に、msvcr100.dll、msvcp100.dll、および libcmt.lib または libcpmt.lib 内に実装されています。PPL と非同期エージェント ライブラリは、ほとんどヘッダーのみの実装です。

高度な並列処理の概念に対応していなかった Visual Studio 2008 とは異なり、Visual Studio 2010 では、Visual Studio デバッガーが ConcRT に対応するため、同時実行の問題をデバッグしやすくなっています。Visual Studio 2010 には同時実行プロファイラーが含まれており、並列アプリケーションの動作を表示できます。デバッガーには、アプリケーションのすべてのタスクの状態と、アプリケーションのコール スタックを表示する新しいウィンドウがあります。図 1 に、[Parallel Tasks] (並列タスク) ウィンドウと [Parallel Stacks] (並列スタック) ウィンドウを示します。

Figure 1 Parallel Stacks and Parallel Tasks Debug Windows
図 1 デバッグ用の [Parallel Tasks] (並列タスク) ウィンドウと [Parallel Stacks] (並列スタック) ウィンドウ

IntelliSense と設計時の生産性

Visual Studio 2010 には、新しい IntelliSense と参照のインフラストラクチャが含まれています。このインフラストラクチャの強化により、大規模コード ベースを使用するプロジェクトの拡張性と応答性が向上するだけでなく、新しい設計時の生産性機能がいくつか実現されます。

随時表示されるエラー報告やクイック ヒントのツールヒントなどの IntelliSense 機能は、新しいコンパイラ フロント エンドに基づいています。これにより、コード ファイルが変更されている場合でも、完全な翻訳単位を解析して、コードのセマンティクスに関する豊富で正確な情報が提供されます。

クラス ビューやクラス階層などのすべてのコード参照機能では、SQL データベースに格納されているソース コード情報を使用するようになっているため、インデックスの作成が可能になり、メモリの使用量が固定になります。以前のリリースと違って、Visual Studio 2010 の IDE は常に応答性が高いので、ヘッダー ファイルを変更したときでも、コンパイル単位が再度解析されるまで待機する必要がなくなりました。

IntelliSense の随時表示されるエラー報告 (おなじみの赤い波線) では、コードの参照中と編集中に、コンパイラ並みの構文エラーとセマンティック エラーが表示されます。エラー箇所をマウスでポイントすると、エラー メッセージが表示されます (図 2 参照)。また、[Error List] (エラー一覧) ウィンドウには、現在表示されているファイルのエラーと、コンパイル単位の他の場所で発生している IntelliSense エラーが表示されます。こうした情報のすべては、ビルドを行うことなく表示できます。

Figure 2 Live Error Reporting Showing IntelliSense Errors
図 2 IntelliSense エラーを示す随時表示されるエラー報告

また、「#include」と入力すると、関連インクルード ファイルの一覧がドロップダウン リストに表示され、入力するにつれて一覧が絞り込まれます。

新しい [Navigate To] (移動) 機能は、ファイルやシンボルを検索する際の生産性を向上するのに役立ちます (この機能は、[Edit] (編集) メニューの [Navigate To] (移動) をクリックするか、Ctrl キーを押しながらコンマ (,) キーを押して使用できます)。この機能を使用すると、入力する部分文字列に基づいて検索結果がリアルタイムに表示され、すべてのプロジェクトにまたがるシンボルとファイルに対して入力文字列が照会されます (図 3 参照)。また、この機能は C# ファイルと Visual Basic ファイルでも使用でき、拡張も可能です。

Figure 3 Using the Navigate To Feature
図 3 [Navigate To] (移動) 機能の使用

呼び出し階層では、特定の関数から呼び出されたすべての関数、または特定の関数を呼び出すすべての関数に移動できます (この機能は、Ctrl+KCtrl+T、または右クリックで表示されるメニューを使用して起動できます)。これは、以前のバージョンの Visual Studio に存在していた "呼び出しブラウザー" 機能が強化されたバージョンです。[Call Hierarchy] (呼び出し階層) ウィンドウは以前よりもはるかにわかりやすく構成されていて、任意の関数の呼び出し元と任意の関数からの呼び出し先の両方を表すツリーが、同じウィンドウに表示されます。

すべてのコード参照機能は、純粋な C++ と C++/CLI の両方で使用できますが、IntelliSense 関連の機能 (随時表示されるエラー報告やクイック ヒントなど) は、Visual Studio 2010 の最終リリースに含まれる C++/CLI には使用できません。

このリリースでは、その他の関連付けを行うエディター機能も強化されています。たとえば、ソリューション全体に含まれるコード要素 (クラス、クラス メンバー、関数など) への参照を検索するのに使用されていた、おなじみの "すべての参照の検索" 機能はより柔軟性を増し、右クリックで表示されるコンテキスト メニューの [Resolve Results] (結果の解決) を使用して、検索結果をさらに絞り込めるようになっています。

非アクティブなコードは、灰色になるのではなく、色づけを保持することによって、セマンティクス情報を保持するようになっています。図 4 は、淡色表示になっている非アクティブなコードでも、別の色を使用してセマンティクス情報が表示されるようすを示しています。

Figure 4 Inactive Code Blocks Retain Colorization
図 4 色づけを保持する非アクティブなコード ブロック

Visual Studio 2010 では、既に説明した機能のほかに、一般的なエディター エクスペリエンスも強化されています。新しい Windows Presentation Foundation (WPF) ベースの IDE は、外観をすっきりさせ、読みやすさを向上するように設計し直されています。コード エディターやデザイン ビューなどのドキュメント ウィンドウは、固定しない状態で IDE のメイン ウィンドウの外側に配置できるようになり、マルチモニター表示が可能です。Ctrl キーとマウス ホイールを使用すると、コード エディター ウィンドウを簡単に拡大および縮小できます。IDE では、拡張性のサポートも強化されています。

ビルド システムとプロジェクト システム

Visual Studio 2010 では、C++ プロジェクトのビルド システムとプロジェクト システムも大幅に強化されています。

最も重要な変更点は、C++ プロジェクトのビルドに MSBuild が使用されるようになったことです。MSBuild は、拡張可能な XML ベースのオーケストレーション エンジンで、以前のバージョンの Visual Studio では C# プロジェクトと Visual Basic プロジェクトで使用されていました。MSBuild は、すべての言語に共通のマイクロソフト ビルド システムになっており、ビルド ラボでも開発者個人のコンピューターでも使用できます。

C++ のビルド プロセスは、MSBuild ターゲットとタスク ファイルの観点から定義するようになったため、カスタマイズ性、制御性、および透過性がはるかに高くなります。

C++ プロジェクト タイプには、.vcxproj という新しい拡張子が付加されます。Visual Studio では、従来の .vcproj 拡張子が付いたファイルとソリューションが、新しい形式に自動的にアップグレードされます。また、vcupgrade.exe コマンド ライン ツールを使用して、1 つのプロジェクトをコマンド ラインからアップグレードできます。

これまでは、現在使用している Visual Studio バージョンで提供されているツールセット (コンパイラやライブラリなど) しか使用できませんでした。新しい IDE の使用を開始する前には、新しいツールセットに移行する準備が整うまで待つ必要がありました。Visual Studio 2010 では、ツールセットの複数のバージョンを対象にビルドできるようにすることで、この問題が解決されます。たとえば、Visual Studio 2010 で作業しながら、Visual C++ 9.0 のコンパイラとライブラリを対象にすることもできます。図 5 は、プロパティ ページに表示される複数バージョン対応のネイティブ設定を示しています。

5 Targeting Multiple Platform Toolsets
図 5 プラットフォームの複数のツールセットの対象指定

MSBuild を使用すると、C++ ビルド システムの拡張性が大きく向上します。ビルド システム既定の機能が不十分で要件を満たせなければ、独自のツールやその他のビルド手順を追加して、ビルド システムを拡張できます。MSBuild では、実行可能コードの再利用可能な単位としてタスクを使用し、ビルド操作を実行します。独自のタスクを作成し、このタスクを XML ファイルで定義すれば、ビルド システムを拡張できます。MSBuild は、これらの XML ファイルからタスクをその場で生成します。

既存のプラットフォームとツールセットは、追加手順用の .props ファイルと .targets ファイルを、ImportBefore フォルダーと ImportAfter フォルダーに追加することで拡張できます。これは、既存のビルド システムを拡張する必要があるライブラリやツールのプロバイダーにとっては特に有用です。プラットフォームに独自のツールセットを定義することもできます。また、MSBuild では優れた診断情報が提供されるため、ビルドで生じた問題をデバッグしやすくなり、インクリメンタル ビルドの信頼性も高まります。そのうえ、ソース管理やビルド ラボにより密接に連携し、開発者のコンピューター構成にあまり依存しないビルド システムを作成することもできます。

ビルド システムの上位に位置するプロジェクト システムでも、MSBuild で実現される柔軟性と拡張性を利用します。プロジェクト システムは、MSBuild プロセスを認識し、MSBuild によって使用可能になった情報が Visual Studio に自動的に表示されるようにします。

カスタマイズは、プロパティ ページで表示および構成できます。独自のプラットフォーム (既存の x86 プラットフォームや x64 プラットフォームなど) や独自のデバッガーを使用するようにプロジェクト システムを構成することができます。プロパティ ページでは、コンポーネントを記述および統合して、コンテキストに依存するプロパティの値を動的に更新することができます。Visual Studio 2010 のプロジェクト システムでは、プロパティ ページを使用しないで、独自のカスタム UI を作成してプロパティの読み取りと書き込みを行うこともできます。

コンパイルの高速化とパフォーマンスの向上

これまで説明してきた設計時のエクスペリエンスの向上に加えて、Visual Studio 2010 では、複数のコードを生成する拡張機能がコンパイラ バックエンドに追加されたため、Visual C++ のコンパイラを使用してビルドされるアプリケーションのコンパイルの速度、品質、およびパフォーマンスも向上します。

特定のアプリケーションのパフォーマンスは、作業セットによって異なります。今回のリリースでは、複数の最適化を行うことによって、x64 アーキテクチャのコード サイズが 3 ~ 10% 減少しているため、x64 アプリケーションのパフォーマンスが向上します。

ゲーム、オーディオ、ビデオ、およびグラフィックスの開発者にとって重要な単一命令複数データ (SIMD: Single Instruction Multiple Data) コードの生成は、パフォーマンスとコードの品質を向上するために最適化されています。この機能強化には、不適切な依存関係の破棄、定数 vector の初期化のベクトル化、XMM レジスタの適切な割り当てなどによって、冗長な読み込み、保存、および移動を取り除くことも含まれます。また、__mm_set_**、__mm_setr_**、および __mm_set1_** の組み込みファミリが最適化されています。

パフォーマンスを向上するために、アプリケーションでは、リンク時のコード生成 (LTCG) とガイド付き最適化のプロファイル (PGO) を使用してビルドします。

x64 プラットフォームのコンパイル速度は、x64 のコード生成を最適化することによって向上しています。LTCG を使用したコンパイル (最適化の強化のために推奨されます) では、LTCG を使用しないコンパイルよりも時間がかかるのが一般的です。これは、大規模アプリケーションをコンパイルする場合に特に顕著に表れます。Visual Studio 2010 では、LTCG を使用するコンパイル速度が最大 30% 向上しています。今回のリリースでは、PDB ファイルを記述するための専用スレッドが導入されているため、/DEBUG スイッチを使用する場合に、リンク時間が短縮していることがわかるでしょう。

PGO インストルメンテーションの実行速度は、インストルメント化されたバイナリのロックなしのバージョンのサポートを追加することで高速化されています。また、PogoSafeMode という新しい POGO オプションもあります。これを使用して、アプリケーションを最適化する際に、セーフ モードと高速モードのどちらを使用するかを指定できます。高速モードは既定の動作です。セーフ モードはスレッド セーフですが、高速モードよりも低速です。

コンパイラによって生成されるコードの品質が向上しています。Advanced Vector Extensions (AVX) が完全にサポートされるようになりました。これは、組み込みオプションおよび /arch:AVX オプションを通じて、AMD や Intel のプロセッサで浮動小数点数を集中的に使用するアプリケーションにとっては非常に重要です。/fp:fast オプションを使用すると、浮動小数点計算の精度が向上します。

Windows 7 用アプリケーションをビルドする

Windows 7 には、新しく興味深いテクノロジと機能がいくつか導入され、新しい API が追加されています。Visual Studio 2010 では、このすべての新しい Windows API を使用できます。Visual Studio 2010 には、ネイティブ Windows API 用コードの記述に必要だった Windows SDK のコンポーネントが含まれています。Visual Studio 2010 で使用できる SDK ヘッダーやライブラリを使用して、Direct3D 11、DirectWrite、Direct2D、Windows Web サービス API などの革新テクノロジを使用できます。

Visual Studio の今回のリリースでは、開発者がすべての Windows API を使用できるだけでなく、強化された MFC を使用して Windows 用アプリケーションをより簡単に作成することもできます。MFC ライブラリを使用すると、ネイティブ API に直接記述する必要なく、Windows 7 のかなりの機能を使用できます。既存の MFC アプリケーションは再コンパイルするだけで、Windows 7 でも機能します。新しいアプリケーションでは、Windows 7 の新機能を最大限に活用できます。

MFC では、Windows シェルとの統合が強化されています。今回のリリースで追加されているプレビュー、縮小表示、および検索のためのファイル ハンドラーを使用することで、アプリケーションとエクスプローラーをはるかに適切に統合できるようになりました。これらの機能は、図 6 に示す MFC アプリケーション ウィザードでオプションとして提供されます。MFC は、これらのハンドラーを実装する ATL DLL プロジェクトを自動生成します。

Figure 6 MFC Application Wizard with File Handler Options
図 6 ファイル ハンドラーのオプションが表示される MFC アプリケーション ウィザード

Windows 7 で最も目を引くユーザー インターフェイスの変更点の 1 つは、新しいタスク バーです。MFC では、ジャンプ リスト、タブ化された縮小表示、縮小表示プレビュー、進行状況バー、アイコン オーバーレイなどの機能を簡単に使用できます。図 7 に、タブ化された MDI MFC アプリケーションの縮小表示プレビューとタブ化された縮小表示を示します。

Figure 7 Tabbed Thumbnail and Thumbnail Preview in an MFC Application
図 7 MFC アプリケーションのタブ化された縮小表示と縮小表示プレビュー

リボンの UI に Windows 7 形式のリボンも含まれるようになったので、アプリケーションの開発中に、図 8 に示す [Style] (スタイル) ボックスを使用して、アプリケーションの UI を、さまざまな Office 形式のリボンから Windows 7 リボンまで、その場でいつでも切り替えることができます。

Figure 8 Ribbon-Style Dropdown in an MFC Application
図 8 MFC アプリケーションのリボンを選択する [Style] (スタイル) ボックス

MFC では、アプリケーションをマルチタッチ対応にすることが可能で、さまざまなタッチ イベントが発生したときに適切なメッセージが呼び出されて処理されます。タッチ イベントとジェスチャ イベントを登録するだけで、それらのイベントがアプリケーションにルーティングされます。また、MFC では、アプリケーションが既定で高 DPI 対応になるため、アプリケーションが高 DPI 画面に適合し、構成しているピクセルが見えたり、ぼやけて見えたりすることはありません。MFC は、フォントやその他の要素を内部的に拡張および変更して、高 DPI 画面でも依然として UI がくっきりと表示されるようにします。

新しい Windows 7 の機能に加えて、(Windows Vista 以降に存在していましたが、以前の MFC のリリースには含まれていなかった) その他の Windows 機能もいくつか含まれています。たとえば、"再起動マネージャー" は、Windows Vista で導入された便利な機能で、アプリケーションの終了前にアプリケーションを保存できます。アプリケーションは再起動時にこの機能を呼び出して、状態を復元できます。MFC アプリケーションで再起動マネージャーを最大限に活用して、異常終了や再起動をよりスムーズに処理できるようになります。次のコード行を追加するだけで、既存のアプリケーションの再起動と回復を有効にできます。

CMyApp::CMyApp() {
  m_dwRestartManagerSupportFlags = 
    AFX_RESTART_MANAGER_SUPPORT_RESTART;
// other lines of code ...
}

MFC アプリケーション ウィザードを使用するだけで、新しい MFC アプリケーションからこの機能を自動的に使用できます。アプリケーションで自動保存のメカニズムを使用してドキュメントを保存することができ、自動保存の間隔はユーザーが定義できます。MFC アプリケーション ウィザードでは、再起動のサポートかアプリケーションの回復の開始 (Doc/View タイプのアプリケーションに適用されます) のみを選択できます。

その他に、Windows のタスク ダイアログ ボックスが追加されています。これは、強化された種類のメッセージ ボックスです (図 9 参照)。MFC には、アプリケーションで使用できる、タスク ダイアログ ボックスのラッパーが含まれるようになりました。

Figure 9 Task Dialog
図 9 タスク ダイアログ ボックス

MFC クラス ウィザードの復活

今回のリリースでは、MFC ライブラリに新機能が追加されただけでなく、Visual Studio IDE で MFC をより簡単に使用できるようになりました。最もよく使用される機能の 1 つであった MFC クラス ウィザードが復活し、改良が加えられています (図 10 参照)。MFC クラス ウィザードを使用して、クラス、イベント ハンドラーなどの要素をアプリケーションに追加できるようになります。

Figure 10 MFC Class Wizard
図 10 MFC クラス ウィザード

ほかにも、リボン デザイナーが追加されています。これを使用すると、リボン UI を (Visual Studio 2008 のようにコードで定義するのではなく) 視覚的にデザインして、XML リソースとして保存できます。このデザイナーは、新しいアプリケーションを作成するのに便利なのはもちろんのこと、既存のアプリケーションでも、このデザイナーを使用して UI を更新できます。リボン UI の既存のコード定義に、次のコード行を一時的に追加するだけで、XML 定義を作成できます。

m_wndRibbonBar.SaveToXMLFile(L"YourRibbon.mfcribbon-ms");

結果として作成された XML ファイルはリソース ファイルとして使用することができ、リボン デザイナーを使用してさらに変更を加えることができます。

まとめ

Visual Studio 2010 は、Visual C++ の進歩がしていく中での大きなリリースで、多くの点で、開発者たちの生活をさらに楽にします。今回の記事では、C++ に関連するさまざまな機能強化についてほんの少し紹介したにすぎません。さまざまな機能の詳細については、MSDN ドキュメントと Visual C++ チームのブログ (blogs.msdn.com/vcblog、英語) を参照してください。このブログの内容は、この記事のいくつかのセクションの基礎として使用されています。

Sumit Kumar は、Visual C++ IDE チームのプログラム マネージャーです。テキサス大学ダラス校で、コンピューター サイエンスの理学修士号を取得しました。

この記事のレビューに協力してくれた技術スタッフの Stephan T. Lavavej、Marian Luparu、および Tarek Madkour に心より感謝いたします。