Visual C++ での互換性に影響する変更点
Visual C++ コンパイラの新しいバージョンにアップグレードすると、以前にコンパイルと動作が正常に行えたコードでコンパイルやランタイム エラーが発生する場合があります。新しいバージョンでこのような問題を引き起こす変更は互換性に影響する変更と呼ばれ、通常は C++ 言語の基準、関数シグネチャ、またはメモリ オブジェクトのレイアウトの変更によって必要となります。
Visual C++ では、POD (plain old data) オブジェクト レイアウトおよび COM インターフェイスはバージョンごとに互換性が失われないようになっていますが、継承またはテンプレートのインスタンス化が関係するシナリオなどでは、オブジェクト レイアウトが変更する可能性があります。
検出や診断が難しいランタイム エラーを回避するには、異なるバージョンのコンパイラを使用してコンパイルされたバイナリには静的にリンクしないことをお勧めします。また、EXE または DLL プロジェクトをアップグレードする場合、リンクするライブラリもアップグレードします。CRT (C Runtime) または STL (Standard Template Library) 型を使用している場合は、異なるバージョンのコンパイラを使用してコンパイルされたバイナリ間 (DLL を含む) で渡さないでください。詳細については、「DLL の境界を越えて CRT オブジェクトを渡す場合に発生する可能性のあるエラー」を参照してください。
さらに、COM インターフェイスまたは POD オブジェクトではないオブジェクトで、特定のレイアウトに依存するコードを記述しないことをお勧めします。このようなコードを記述している場合、アップグレード後に動作することを確認する必要があります。詳細については、「ABI の境界での移植性 (Modern C++)」を参照してください。
ここからは、Visual Studio 2013 の Visual C++ の特定の互換性に影響する変更点について説明します。
Visual C++ コンパイラ
final のキーワードは、以前はコンパイルできましたが、現在は未解決のシンボル エラーを生成します。
struct S1 { virtual void f() = 0; }; struct S2 final : public S1 { virtual void f(); }; int main(S2 *p) { p->f(); }
呼び出しが仮想呼び出しであったことが原因で、以前のバージョンではエラーは発生しませんでした。とはいえ、このプログラムは実行時にクラッシュする結果になりました。現在は、クラスが final であると認識されるため、リンカー エラーが発生します。この例でエラーを解決するには、S2::f の定義を含む obj に対してリンクを行うことになります。
コンパイラは現在 ISO C++ 標準に準拠しているため、名前空間の中でフレンド関数を使用する場合は、そのフレンド関数を参照する前に再宣言する必要があります。そうしない場合は、エラーが発生します。たとえば、次はコンパイルされなくなります。
namespace NS { class C { void func(int); friend void func(C* const) {} }; void C::func(int) { NS::func(this); // error } }
このコードを修正するには、次のようにフレンド関数を宣言します。
namespace NS { class C { void func(int); friend void func(C* const) {} }; void func(C* const); // conforming fix void C::func(int) { NS::func(this); } }
C++ 標準では、クラス内で明示的な特殊化は許可されません。Visual C++ では、特定の状況でこの作業を実行できますが、次の例のような状況では、現在はエラーが生成されます。コンパイラが、2 番目の関数が最初の関数の特殊化であることを認識しないのが原因です。
template <int N> class S { public: template void f(T& val); template <> void f(char val); }; template class S<1>;
このコードを修正するには、2 番目の関数を変更します。
template <> void f(char& val);
Visual C++ では、次の例に示す 2 つの関数のあいまいさの解消が行われなくなりました。現在ではエラーが出力されます。
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(); // error }
このコードを修正するには、呼び出しを明確にします。
template<typename T> void Func(T* t = nullptr); template<typename T> void Func(...); int main() { Func<int>(nullptr); // ok }
コンパイラが C++11 に準拠するまで次のコードはコンパイルされ、x を int 型に解決していました。
auto x = {0}; int y = x;
現在は、このコードは x を std::initializer_list<int> 型に解決し、x を int 型に代入しようとする次の行でエラーが発生します (既定では変換は行われません)。 このコードを修正するには、int を使って auto を置換します。
int x = {0}; int y = x;
初期化の際に右辺値の型が左辺値の型と一致しない場合集約の初期化は許可されなくなり、ISO C++11 基準では縮小変換を使用しない場合は均一な初期化が必要なためエラーが発行されます。以前は、縮小変換が使用できる場合、エラーの代わりに C4242 警告が発行されていました。
int i = 0; char c = {i}; // error
このコードを修正するには、明示的な縮小変換を追加します。
int i = 0; char c = {static_cast<char>(i)};
次の初期化が廃止されました。
void *p = {{0}};
このコードを修正するには、次の形式のどちらかを使用します。
void *p = 0; // or void *p = {0};
名前参照が変更されました。 次のコードは、Visual Studio 2012 の Visual C++ と Visual Studio 2013 の Visual C++ では異なる方法で解決されます。
enum class E1 {a}; enum class E2 {b}; int main() { typedef E2 E1; E1::b; }
Visual Studio 2012 の Visual C++では、式 E1 に含まれる E1::b は、グローバル スコープ内の ::E1 に解決されていました。Visual Studio 2013 の Visual C++ では、式 E1 に含まれる E1::b は typedef E2 内で main() 定義に解決され、型は ::E2 になります。
オブジェクトのレイアウトが変更されました。 x64 では、クラスのオブジェクト レイアウトが以前のリリースから変更される場合があります。仮想関数が存在するものの仮想関数を持つ基底クラスがない場合は、コンパイラのオブジェクト モデルは、データ メンバーのレイアウトの後で仮想関数テーブルにポインターを挿入します。これは、レイアウトがすべての場合に最適となるわけではないことを意味します。以前のリリースでは、x64 の最適化によってレイアウトが調整されましたが、複雑なコードでは正常に機能しなかったため、Visual Studio 2013 の Visual C++ では削除されました。たとえば、次のコードについて考えます。
__declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
Visual Studio 2013 の Visual C++ では、x64 での sizeof(S2) の結果は 48 になりますが、以前のリリースでは 32 と評価されました。これを x64 の Visual Studio 2013 の Visual C++ で 32 と評価されるようにするには、仮想関数があるダミー基底クラスを追加します。
__declspec(align(16)) struct S1 { }; struct dummy { virtual ~dummy() {} }; struct S2 : public dummy { virtual ~S2(); void *p; S1 s; };
以前のリリースでは最適化の対象となったコードの場所を探すには、該当するリリースのコンパイラと /W3 のコンパイラ オプションを併用し、警告 4370 をオンにします。次に例を示します。
#pragma warning(default:4370) __declspec(align(16)) struct S1 { }; struct S2 { virtual ~S2(); void *p; S1 s; };
Visual Studio 2013 の Visual C++ 以前の Visual C++ コンパイラでは、このコードは次のメッセージを出力します。
warning C4370: 'S2' : layout of class has changed from a previous version of the compiler due to better packing
x86 コンパイラでは、すべてのバージョンの Visual C++ で同じ標準以下のレイアウト問題があります。たとえば、次のコードが x86 でコンパイルされた場合を考えます。
struct S { virtual ~S(); int i; double d; };
sizeof(S) の結果は 24 です。しかし、これは先ほど説明した x64 の代替手段を使用すると 16 に減らせます。
struct dummy { virtual ~dummy() {} }; struct S : public dummy { virtual ~S(); int i; double d; };
Visual C++ のライブラリ
標準テンプレート ライブラリ
新しい最適化とデバッグのチェックを有効にするために、C++ 標準ライブラリの Visual Studio の実装では、バイナリの互換性がバージョンごとに意図的に保たれていません。そのため、C++ 標準ライブラリを使用すると、異なるバージョンを使用してコンパイルされたオブジェクト ファイルとスタティック ライブラリは 1 つのバイナリ (EXE または DLL) に混在させることができず、C++ 標準ライブラリ オブジェクトは異なるバージョンを使用してコンパイルされたバイナリ間で渡すことができません。混在があると、_MSC_VER の不一致に関するリンカー エラーが発生します (_MSC_VER はコンパイラのメジャー バージョンを含むマクロです。たとえば Visual Studio 2013 の Visual C++ では 1800 です)。 このチェックでは、DLL の混在を検出できず、Visual C++ 2008 以前のバージョンが関係する混在も検出できません。
Visual Studio 2013 の Visual C++ は、Visual C++ 2010 で実装された _ITERATOR_DEBUG_LEVEL の不一致を検出し、RuntimeLibrary の不一致も検出します。これらは、コンパイラ オプション /MT (静的なリリース)、/MTd (静的なデバッグ)、/MD (動的なリリース)、および /MDd (動的なデバッグ) が混在する場合に発生します。詳細については、「Visual C++ 2012 での互換性に影響する変更点」を参照してください。
コードが、以前のリリースのシミュレートされたエイリアスのテンプレートを検出した場合、変更する必要があります。たとえば、allocator_traits<A>::rebind_alloc<U>::other の代わりに、allocator_traits<A>::rebind_alloc<U> と指定する必要があります。ratio_add<R1, R2>::type は必要でなくなり、ratio_add<R1, R2> を使うことが勧められていますが、前者でもコンパイルは可能です。これは、ratio<N, D> を使用して圧縮するには、typedef "型" が必要であるためです (既に圧縮されている場合も同じ型を使用します)。
#include <algorithm> または std::min()を呼び出すときに、std::max() を使用する必要があります。
既存のコードが、以前のリリースのシミュレートされたスコープを指定された列挙型、つまり名前空間の中でラップされている、スコープを指定されていない従来の列挙型を使用している場合は、そのコードを変更する必要があります。たとえば、型 std::future_status::future_status を参照していた場合は、std::future_status を指定する必要があります。ただし、ほとんどのコードは影響を受けません。たとえば、std::future_status::ready は引き続きコンパイルされます。
explicit operator bool() は operator unspecified-bool-type() より厳格です。explicit operator bool() を使用すると、bool に明示的に変換できます。たとえば、shared_ptr<X> sp を使用している場合、static_cast<bool>(sp) と bool b(sp) の両方が有効です。また、bool、if (sp)、!sp などのブール値検証可能な sp && whatever への "状況依存型変換" も用意されています。ただし、explicit operator bool() は bool への暗黙の変換を禁止するため、bool b = sp と指定することはできません。また bool を戻り値の型として指定したり、return sp を指定したりすることもできません。
現在は、実際の可変個引数テンプレートが実装されているため、_VARIADIC_MAX と関連マクロは何の影響も及ぼしません。 依然として _VARIADIC_MAX を定義している場合は、無視されます。 シミュレートされた可変個引数テンプレートを他の方法でサポートすることを意図して Microsoft のマクロ メカニズムを活用していた場合は、コードを変更する必要があります。
**通常のキーワードに加えて、**STL ヘッダーは状況依存のキーワード "override" と "final" のマクロ化を禁止するようになりました。
reference_wrapper/ref()/cref() は、一時オブジェクトへのバインディングを禁止するようになります。
<random> は、コンパイル時の事前条件を厳密に実装するようになりました。
さまざまな STL 型の特徴は、"T は完全な型であるものとする" という事前条件を持つことです。コンパイラは、このことをより厳密に実装するようになりましたが、あらゆる状況でこのことを強制するとは限りません (STL 事前条件に違反すると、未定義の動作がトリガーされるため、標準では、強制を保証していません)。
STL は /clr:oldSyntax をサポートしていません。
C++11 仕様のcommon_type では、予期しない結果が生じることがありました。特に、common_type<int, int>::type が int&& を返すことは問題でした。そこで、Visual C++ では「ライブラリ ワークグループの懸案事項 2141 に対して推奨される解決」が実装され、common_type<int, int>::type が int を返すようになりました。
この変更の副作用として、ID の場合に機能しなくなりました。common_type<T> が常に型 T になるとは限らないからです。これは、上記の「推奨される解決」に準拠していますが、コードの互換性に影響があります。
ID 型の特徴が必要な場合、std::identity で定義された非標準の <type_traits> は <void> で機能しないので使用しないでください。代わりに、要件に応じた独自の ID 対応の動作を実装します。次に例を示します。
template <typename T> struct Identity { typedef T type; };
MFC と ATL
Unicode が非常に一般的になり、MBCS の使用が大幅に減少しているため、MFC MBCS ライブラリはもう Visual Studio に含まれていません。この変更により、新しいコントロールとメッセージの多くは Unicode 専用になったため、MFC は Windows SDK 自体により緊密に整合するようになりました。ただし、MFC の MBCS ライブラリをどうしても引き続き使用する必要がある場合は、MSDN ダウンロード センター からダウンロードできます。Visual C++ 再頒布可能パッケージにも、引き続きこのライブラリが含まれています。
MFC リボンのアクセシビリティが変更されました。 1 レベルのアーキテクチャの代わりに、階層アーキテクチャが採用されました。 CRibbonBar::EnableSingleLevelAccessibilityMode() を呼び出して、引き続き古い動作を使用することもできます。
CDatabase::GetConnect メソッドが削除されました。 セキュリティを強化するために、接続文字列は暗号化して保存され、必要な場合のみ暗号化を解除されるようになりました。この文字列をプレーンテキストとして返すことはできません。 CDatabase::Dump メソッドを使用してこの文字列を取得できます。
CWnd::OnPowerBroadcast のシグネチャが変更されました。 このメッセージ ハンドラーのシグネチャは、2 番目のパラメーターとして LPARAM を受け取るように変更されました。
メッセージ ハンドラーに対応するためにシグネチャが変更されました。 新しく追加された ON_WM_* メッセージ ハンドラーを使用するために、次の関数のパラメーター リストが変更されました。
CWnd::OnDisplayChange では、新しい (UINT, int, int) マクロをメッセージ マップ内で使用できるように、(WPARAM, LPARAM) が ON_WM_DISPLAYCHANGE に変更されました。
CFrameWnd::OnDDEInitiate では、新しい (CWnd*, UINT, UNIT) マクロをメッセージ マップ内で使用できるように、(WPARAM, LPARAM) が ON_WM_DDE_INITIATE に変更されました。
CFrameWnd::OnDDEExecute では、新しい (CWnd*, HANDLE) マクロをメッセージ マップ内で使用できるように、(WPARAM, LPARAM) が ON_WM_DDE_EXECUTE に変更されました。
CFrameWnd::OnDDETerminate では、新しい (CWnd*) マクロをメッセージ マップ内で使用できるように、パラメーターとして (WPARAM, LPARAM) が ON_WM_DDE_TERMINATE に変更されました。
CMFCMaskedEdit::OnCut では、新しい (WPARAM, LPARAM) マクロをメッセージ マップ内で使用できるように、ON_WM_CUT からパラメーター不使用に変更されました。
CMFCMaskedEdit::OnClear では、新しい (WPARAM, LPARAM) マクロをメッセージ マップ内で使用できるように、ON_WM_CLEAR からパラメーター不使用に変更されました。
CMFCMaskedEdit::OnPaste では、新しい (WPARAM, LPARAM) マクロをメッセージ マップ内で使用できるように、ON_WM_PASTE からパラメーター不使用に変更されました。
#ifdef は MFC ヘッダー ファイルから削除されました。 MFC ヘッダー ファイル内に存在していた、サポートされていないバージョンの Windows (#ifdef) に関連する多数の WINVER < 0x0501 が削除されました。
ATL DLL (atl120.dll) が削除されました。 ATL は、ヘッダーおよび 1 つのスタティック ライブラリ (atls.lib) として提供されるようになりました。
atlsd.lib、atlsn.lib、および atlsnd.lib が削除されました。 atls.lib は、文字セットに対する依存関係を持たず、デバッグ/リリースに固有のコードも含まなくなりました。これは、Unicode/ANSI のどちらでも、またデバッグ/リリースのどちらでも同じ動作をするため、このライブラリのただ 1 つのバージョンが必要とされるようになりました。
ATL DLL と共に ATL/MFC Trace Tool が削除され、トレース機構が簡略化されました。CTraceCategory コンストラクターは 1 つのパラメーター (カテゴリ名) を受け取り、TRACE マクロは CRT のデバッグ レポート関数を呼び出します。