原文: http://www.boost.org/more/microsoft_vcpp.html

c++boost.gif (8819 bytes) Home Libraries People FAQ More

Portability Hints: Microsoft Visual C++ 6.0 SP4

このページは、portability hints for Borland C++ と同様に、Microsoft Visual C++ version 6.0 service pack 4 コンパイラの特徴についてのヒントを提供する。 広く認められた欠陥のリストは、Microsoft のサポート サイトで見ることができる。

次のリスト中の各見出しは、結果を実証するサンプル ソース コードで完結する個々の問題点に関して述べている。 ここにあるほとんどのサンプル コードは、gcc 2.95.2 と Comeau C++ 4.2.44 でのコンパイルが確認されている。

Preprocessor symbol

全ての Microsoft C++ コンパイラで、_MSC_VER というプリプロセッサのシンボルが定義されている。その値は、コンパイラの内部的なバージョン番号の十進表記になっている。他のいくつかのコンパイラもこのシンボルを定義しているため、boost は、 もしコンパイラが本当に Microsoft Visual C++ であるときに限り、_MSC_VER の値として boost/config.hpp の中で定義されるシンボル BOOST_MSVC を用意している。 以下の一覧表に判明している値を示す。

コンパイラ BOOST_MSVC の値
Microsoft Visual C++ 6.0 (SP4 まで) 1200

Core Language

[chained using] Chaining using-declarations

連鎖した using 宣言が機能しない。
void f();

namespace N {
  using ::f;
}

void g()
{
  using N::f;  // C2873: 'f': シンボルを using 宣言の中で使用することはできません。
}

[explicit-instantiation] Explicit function template instantiation

関数テンプレートを明示的にインスタンス化しようとして、暗黙的に呼ばれる間違った関数を導いてしまう。
#include <stdio.h>

template<class T>
void f()
{
  printf("%d\n", sizeof(T));
}

int main()
{
  f<double>();      // 出力: "1"
  f<char>();        // 出力: "1"
  return 0;
}

[for-scoping] Scopes of definitions in for-loops

for ループ内における変数定義のスコープは、ループの内部だけのローカルなものであるべきなのに、そうでなく一つ外側のブロックでローカルになっている。
int main()
{
  for(int i = 0; i < 5; ++i)
   ;
  for(int i = 0; i < 5; ++i)  // C2374: 'i': 再定義されています。2 回以上初期化されています。
   ;
  return 0;
}
回避手段: 問題となる for ループをもう一つの別の一組の中括弧で囲う.

もう一つの可能な回避手段 (Vesa Karvonen が気付かせてくれた) は次のようなものである。

#ifndef for
#define for if (0) {} else for
#endif
インクルードされたヘッダー内のプラットフォーム依存のインライン関数が、古い形式の for のスコープに依存するかも知れないことに注意。

[inclass-member-init] In-class member initialization

標準として承認されている std::numeric_limits テンプレートの実装に必要な、クラス内でのメンバーの初期化がうまく機能しない。
struct A
{
  static const int i = 5;      // "純粋仮想関数の宣言に構文上の誤りがあります。"
};
回避手段: enum (正しい型とは言えないがコンパイル時に使用できる定数式) を使用するか、またはクラスの外側 (正しい型を使えるがコンパイル時の定数式として使うことは禁止) で定義する。boost ライブラリ中で、どうやって移植性の高いやり方でメンバーとして定数を定義するかのガイドラインを知るには、Coding Guidelines for Integral Constant Expressions を見ること。

[koenig-lookup] Argument-dependent lookup

Koenig の自動参照とも呼ばれている引数依存の参照が、オーバーロードされた演算子のでは機能するが普通の関数では機能しない。引数の型によって引き起こされた付加的な名前空間が考慮されているように見えない。
namespace N {
  struct A {};
  void f(A);
}

void g()
{
  N::A a;
  f(a);     // 'f': 定義されていない識別子です。
}

[template-friend] Templates as friends

クラスの friend としてテンプレートを定義できない。
template<class T>
struct A {};

struct B
{
  template<class T>
  friend struct A;     // "構文エラー"
};

[member-template-outofline] Out-of-line definitions of member templates

メンバーのテンプレートを属するクラスの外で定義するとうまく機能しない。
template<class T>
struct A
{
  template<class U>
  void f();
};

template<class T>
template<class U>   // "構文エラー"
void A<T>::f()      // "T: 定義されていない識別子です。"
{
}
回避手段: メンバーのテンプレートを属するクラス内にインラインで定義する。

[partial-spec] Partial specialization

クラス テンプレートの部分的な特殊化版が機能しない。
template<class T>
struct A {};

template<class T>
struct B {};

template<class T>
struct A<B<T> > {};  // このテンプレート クラスはすでに非テンプレート クラスとして定義されています。
回避手段: インタフェイスが重要でない状況では、クラスのメンバーのテンプレートが部分的な特殊化版をシミュレートできることがある。

[template-value] Dependent template value parameters

既存のテンプレートのパラメータに依存するテンプレート値パラメータは、もし (typename と共に) 正しい文法が使われていても、内部的なコンパイル エラーを引き起こす。
template<class T, typename T::result_type> // C1001: 内部コンパイラ エラー: msc1.cpp, line 1794
struct B {};
 // (omit "typename" and it compiles)

回避手段: "typename" キーワードを使うのを止める。しかしながら、こうすることでそのプログラムは正当であることが保障されなくなる。

[wchar_t] wchar_t is not built-in

The type wchar_t が組み込み型ではない。
wchar_t x;  // "識別名を宣言するのに、型が指定されていません。"
回避手段: Microsoft Visual C++ を使うときには、 boost/config.hpp ヘッダーは、wchar_t を unsigned short として typedef する形で定義している <cstddef> をインクルードする。特記しておくと、このことは、コンパイラが wchar_tunsigned short を、標準では別々の型であることが要求されるのに、別々の型とはみなさず、それゆえ wchar_t を使ってオーバーロードを行った場合には曖昧さが生じる、ということを意味するのである。この状況のときは BOOST_NO_INTRINSIC_WCHAR_T マクロが定義される。

[delete-const-pointer] Deleting const X * does not work

const や volatile で修飾された型へのポインタを delete しようとするとエラーになる。
void f()
{
  const int *p = new int(5);
  delete p;        // C2664: 'const int *' から 'void *' に変換できません。
}
回避手段: 以下のような関数を定義する。
inline void operator delete(const void *p) throw()
{ operator delete(const_cast<void*>(p)); }
それから、operator delete[] 等、std::nothrow な const や volatile 修飾子との結合のための同様の関数。

Standard Library

[clib-namespace] C library names in global namespace instead of std

<c...> ヘッダーのライブラリの名前は、std 名前空間ではなくグローバルな名前空間におかれている。

回避手段: boost/config.hpp ヘッダーでは BOOST_NO_STDC_NAMESPACE を定義する。それは次のように使うことができる。

# ifdef BOOST_NO_STDC_NAMESPACE
    namespace std { using ::abs; using ::fabs; }
# endif

std::size_t と std::ptrdiff_t は余りに一般的に使われるので、これらのための回避手段は既に boost/config.hpp の中に用意されている。


2001-05-04 Jens Maurer
Japanese Translation Copyright © 2003 Fujio Kojima

オリジナルの、及びこの著作権表示が全ての複製の中に現れる限り、この文書の複製、利用、変更、販売そして配布を認める。このドキュメントは「あるがまま」に提供されており、いかなる明示的、暗黙的保証も行わない。また、いかなる目的に対しても、その利用が適していることを関知しない。