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

Portability Hints: Borland C++ 5.5.1

Boost ライブラリにとって、移植性は普遍的な目標である。この目標を達成する第 1 の方法は、 ISO C++ 標準を厳守する事だ。しかし、 ISO C++ は広く複雑な標準であり、ほとんどのコンパイラはまだ ISO C++ に完全には準拠していない。この制限を考慮して移植性を達成するには、一部のコンパイラがまだ完全に実装していない言語機能を熟知するのが賢明だろう。

このページには Borland C++ version 5.5.1 コンパイラの言語機能についての、移植性のヒントが書かれている。更に、 Appendix には Borland C++ version 5.5 の追加の問題点が書かれている。 Borland C++ 5.5.1 は Win32 用のフリーのコマンドラインコンパイラであり、 http://www.borland.com/ から入手できる。

以下のリストの各項目では個々の問題を説明し、その作用を示すサンプルソースコードを付けている。ここのサンプルコードのほとんどは、 gcc 2.95.2 と Comeau C++ 4.2.44 でコンパイルできる事を確認している。

Preprocessor symbol

全ての Borland C++ コンパイラで、プリプロセッサシンボル __BORLANDC__ が定義されている。このシンボルの値は、コンパイラのバージョン番号の 16 進表記になっている。以下の表に、判明している値を示す。

Compiler __BORLANDC__ value
Borland C++ Builder 4 0x0540
Borland C++ Builder 5 0x0550
Borland C++ 5.5 0x0550
Borland C++ 5.5.1 0x0551
Borland C++ Builder 6 0x0560

Core Language

[using-directive] using 指令と using 宣言の混在

using 指令 (名前空間全体が対象) と名前空間レベルの using 宣言 (別の名前空間に属する個々の識別子が対象) を混合して使うと、実際には存在しない曖昧さが生じる。以下が、これを示すコードの一部である。[訳注1]
namespace N {
  int x();
}

using N::x;
using namespace N;

int main()
{
  &x;     // 曖昧なオーバロード
}

[using template] クラステンプレートに対する using 宣言

クラステンプレートの識別子は、他の識別子と同様、 using 宣言の引数として使える。しかし、以下のコードは Borland C++ でコンパイルできない。[訳注1]
template<class T>
class X { };

namespace N
{
  // "特化パラメータを使用しないとテンプレート 'X<T>' を使えない"
  using ::X;
};

[template const arg] 関数テンプレートの const 引数の型推論

テンプレート関数の型推論では、最上位の const を削除すべきである。しかし、以下に示すコードの一部によって、 "f<const int>(int)" が実体化されてしまう。
template<class T>
void f(T x)
{
        x = 1;  // 大丈夫
        (void) &x;
        T y = 17;
        y = 20;  // "const オブジェクトは変更できない(関数 f<const int>(int) )"
        (void) &y;
}

int main()
{
        const int i = 17;
        f(i);
}
boost/rational.hpp ヘッダの gcd() 関数関連で、これが問題になる。

[function address] オーバロードされた関数のアドレスの解決

オーバロードされた関数のアドレスは、一部の文脈で正しく解決されない (std:13.4 [over.over]) 。小さな例を示す。[訳注2]
template<class Arg>
void f( void(*g)(Arg) );

void h(int);
void h(double);

template<class T>
void h2(T);

int main()
{
  void (*p)(int) = h;            // これは通る (std:13.4-1.1)
  void (*p2)(unsigned char) = h2;    // これも通る (std:13.4-1.1)
  f<int>(h2);  // これも通る (std:13.4-1.3)

  // "h(int) からテンプレート特化を生成できない",
  // "f<Arg>(void (*)(int)) に一致するものが見つからない"
  f<double>(h);   // 通るべき (std:13.4-1.3)

  f( (void(*)(double))h);  // C 形式のキャストは通る (std:13.4-1.6 with 5.4)

  // "このコンテキストではオーバロード 'h' が曖昧"
  f(static_cast<void(*)(double)>(h)); // 通るべき (std:13.4-1.6 with 5.2.9)
}
回避手段: オーバロードされた (されうる) 関数のアドレスの決定には、常に C 形式のキャストを使う。

[string conversion] const char * から std::string への変換

テンプレート関数を明示的に実体化する時、引数の const char * 型から std::string 型への暗黙の型変換が失敗する。 (ただし、普通のケースでは成功する) [訳注1]
#include <string>

template<class T>
void f(const std::string & s)
{}

int main()
{
  f<double>("hello");  // "f<T>(char *) に一致するものが見つからない"
}

回避手段: テンプレート関数の明示的な実体化 (これには Microsoft Visual C++ で重大な問題がある) を避け、該当する型のデフォルトコンストラクトされた使用しないダミー引数を渡すようにする。明示的な実体化を使い続けたいならば、代わりに std::string への明示的な型変換を使うか、 const char * 型の引数を取るテンプレート関数を宣言する。

[template value defaults] テンプレート値パラメータに対する依存したデフォルト引数

前のテンプレート引数に依存する式をデフォルトとするテンプレート値パラメータは使えない。[訳注1]
template<class T>
struct A
{
  static const bool value = true;
};

// "テンプレートはクラスまたは関数でなければならない", "宣言の構文エラー"
template<class T, bool v = A<T>::value>
struct B {};

int main()
{
  B<int> x;
}

回避手段: 関連する非型テンプレート引数が実装の詳細のものなら、継承と完全に限定された識別子を使う (例えば、 ::N::A<T>::value) 。

[function partial ordering] 関数テンプレートの半順序

std:14.5.5.2 [temp.func.order] に記述されている、関数テンプレートの半順序が働かない。
#include <iostream>

template<class T> struct A {};

template<class T1>
void f(const A<T1> &)
{
  std::cout << "f(const A<T1>&)\n";
}

template<class T>
void f(T)
{
  std::cout << "f(T)\n";
}

int main()
{
  A<double> a;
  f(a);   // 出力: f(T)  (誤)
  f(1);   // 出力: f(T)  (正)
}
回避手段: そのような関数全てを一律に、値の引数をとるか参照の引数をとるか、どちらか一方で宣言する。

[instantiate memfun ptr] メンバ関数ポインタによる実体化

何らかのテンプレート引数に依存する一部のメンバ関数ポインタでテンプレートを実体化しようとすると、コンパイラが処理できない。[訳注1]
template<class U> class C { };
template<class T>
class A
{
  static const int v = C<void (T::*)()>::value;
};
回避手段: 仲立ちとして typedef を使う。
template<class U> class C { };
template<class T>
class A
{
  typedef void (T::*my_type)();
  static const int v = C<my_type>::value;
};
(David Abrahams 、 Fernando Cacciola 、 Peter Dimov と交換したメールから抜き出したものだ。実際には試していない。)

Library

[cmath.abs] 関数 double std::abs(double) が無い

関数 double std::abs(double) が定義されているべきだが (std:26.5-5 [lib.c.math]) 、定義されていない。[訳注1]
#include <cmath>

int main()
{
  double (*p)(double) = std::abs;  // エラー
}
std::abs(5.1) と書くと、警告無しで int std::abs(int) が使われるので注意する事。

同じような注意事項が、全ての標準数学関数にも当てはまるようだ。 Borland C++ では、全ての標準数学関数の floatlong double のオーバロードが提供されていない。

回避手段: 型のジェネリック性が必要ないなら、 std::fabs を使う。

Appendix: Additional issues with Borland C++ version 5.5

以下の問題を文書化するのは、主に歴史的な理由による。まだ Borland C++ version 5.5 を使っているならば、 version 5.5.1 にアップグレードする事を強く勧める。 version 5.5.1 では、この項に書かれた問題が修正されている。

[inline friend] テンプレートクラス内のインラインフレンド関数

あるクラスのフレンド関数が、フレンド関数宣言より前に宣言されていない時、その関数はクラス定義を囲む名前空間で宣言される。以下に示したコードの一部 (クラステンプレートとフレンド関数のインライン定義の組み合わせ) によって、クラス N::A<int> のフレンドの非テンプレート関数 "bool N::f(int,int)" が宣言 (そして定義) されるべきだ。しかし Borland C++ v5.5 では、関数 f がそれ以前に宣言されている事が求められてしまう。
namespace N {
template<class T>
class A
{
  // "f は 'N' のメンバではない(関数 main() )"
  friend bool f(T x, T y) { return x < y; }
};
}

int main()
{
  N::A<int> a;
}
このテクニックは boost/operators.hpp で広く使われている。このケースではコンパイラの要求に従ってもうまくいかない。その結果、「 1 つのテンプレートを実体化する事で、たくさんのヘルパ関数を名前空間スコープで宣言する」方法が使えなくなるからだ。 BOOST_NO_OPERATORS_IN_NAMESPACE (BOOST_NO_INLINE_FRIENDS_IN_CLASS_TEMPLATES の方がこのケースに合っているかもしれない) を定義すると、この問題の回避手段がとられるが、それはまた別の問題を引き起こす。 [using-template] を参照。


[訳注1] Borland C++ 5.6 (Borland C++ Builder 6) では修正されているようだ。
[訳注2] f<double>(h) の問題は Borland C++ 5.6 で修正されているようだ。


2000-09-30 Jens Maurer

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