Header <boost/call_traits.hpp>

<boost/call_traits.hpp> の内容は、名前空間boostの中で定義されている。

テンプレートクラス call_traits<T> は、関数への、 あるいは関数から T 型のパラメータを引き渡すための "最良の" 方法を選択する。 typedef コレクションは以下のテーブルのように構成される。 call_traits の目的は、"参照の参照" のような問題が発生しない事や、効率的なラメータの引き渡し (を参照) を保証する事だ。 In each case if your existing practice is to use the type defined on the left, then replace it with the call_traits defined type on the right. [どちらの場合も、もし今までのあなたのやり方がtypedefを使う方法であれば、 それを call_traits で定義された型に置き換えられる。]

部分特殊化版や関数テンプレートをサポートしていないコンパイラの場合 call_traits を使用しても効果がないので、注意すること。 この場合、call_traits が定義した型は常に既存の方法と同一となる。 さらにもし、関数テンプレートと部分的でない特殊化版テンプレート だけをサポートしているコンパイラ (例えば Visual C++ 6) の場合 call_traits を配列型では使用できない (参照の参照問題を解決するためには使用できる)。

既存の方法

call_traits の場合

解説

注釈

T
(return by value)

call_traits<T>::value_type

この型は、T 型の"値"を意味する。 これは、関数の返値に使用する。おそらく stored value にも使用出来る。
["stored value"は、コンストラクタが引数で受け取り、メンバ変数として保存される値の事。一般的にはどう訳したら良いんだろう?]

2

T&
(return value)

call_traits<T>::reference

この型は、T 型への参照を意味する。 通常 T& を返す関数で使用する。

1

const T&
(return value)

call_traits<T>::const_reference

この型は T 型へのconst参照を意味する。 通常 const T& を返す関数で使用する。

1

const T&
(function parameter)

call_traits<T>::param_type

この型は、関数へ T 型の値を渡すための "最良の"方法を意味する。

1,3

注釈:

  1. もし T が既に参照だった場合、call_traits はそのような 参照の参照を起こさないように定義する (部分特殊化のサポートが必要)。
  2. もし T が配列型だった場合、call_traits は value_type を "型へのconstポインタ" か "配列型"として定義する (部分特殊化のサポートが必要)。もし value_type を stored value に使用した場合、 そのメンバ変数は"配列へのconstポインタ"となるか、 あるいは配列そのものとなる。 これは、あなたが本当に必要としている良い解答かもしれないし、 そうでないかもしれない (つまり、気を付けて!)。
  3. もし T が小さな組込型かポインタの場合、param_typeT const& ではなく T const として定義される。こうすることで、関数内のループで引数を使用している場合に コンパイラの最適化能力を向上させる事が出来る。 the semantics of the passed parameter is otherwise unchanged (部分特殊化のサポートが必要)。

 

Copy Constructibility(コピーコンストラクト可能か)

以下の表は、どの call_traits の型から別の call_traits 型へコピーコンストラクトできるかを表している。 those entries marked with a '?' are true only if and only if T is copy constructible: ['?'は、T が Copy Constructible(コピーコンストラクト可能)な場合に真となる。]

 

To:

From:

T

value_type

reference

const_reference

param_type

T

?

?

Y

Y

Y

value_type

?

?

N

N

Y

reference

?

?

Y

Y

Y

const_reference

?

N

N

Y

Y

param_type

?

?

N

N

Y

 

If T is an assignable type the following assignments are possible: [もし、T が Assignable な型ならば、以下のようになる。]

 

To:

From:

T

value_type

reference

const_reference

param_type

T

Y

Y

-

-

-

value_type

Y

Y

-

-

-

reference

Y

Y

-

-

-

const_reference

Y

Y

-

-

-

param_type

Y

Y

-

-

-

 

以下は、コンパイラが部分特殊化をサポートしている場合に call_traits がどのように型を保持しているかを表す。 もし、部分特殊化のサポートがない場合、全ての型は "myclass" と同じように振る舞い、call_traits を参照や配列に対して使用することはできない。

 

call_traits 内での型

オリジナルの型T

value_type

reference

const_reference

param_type

適用

myclass

myclass

myclass&

const myclass&

myclass const&

全 ユーザー定義型

int

int

int&

const int&

int const

全 小組込型

int*

int*

int*&

int*const&

int* const

全 ポインタ型

int&

int&

int&

const int&

int&

全 参照型

const int&

const int&

const int&

const int&

const int&

全 const参照型

int[3]

const int*

int(&)[3]

const int(&)[3]

const int* const

全 配列型

const int[3]

const int*

const int(&)[3]

const int(&)[3]

const int* const

全 const配列型

 

例 1:

以下のクラスは trivial class that 任意の型 T の値を格納できるごく普通のクラスである (call_traits_test.cppを参照)。 このコードは call_traits の typedef が、それぞれどのように使用されるかを表している:

template <class T>
struct contained
{

   // まずtypedefを行う。配列の場合は値そのものが保持されるので、
   // value_type は result_type とは異なる:
   typedef typename boost::call_traits<T>::param_type       param_type;
   typedef typename boost::call_traits<T>::reference        reference;
   typedef typename boost::call_traits<T>::const_reference  const_reference;
   typedef T                                                value_type;
   typedef typename boost::call_traits<T>::value_type       result_type;

   // 値の保持:
   value_type v_;
   
   // コンストラクタ:
   contained() {}
   contained(param_type p) : v_(p){}
   // 値返し:
   result_type value() { return v_; }
   // 参照返し:
   reference get() { return v_; }
   const_reference const_get()const { return v_; }
   // 値渡し:
   void call(param_type p){}

};

例 2 (参照の参照問題):

std::binder1st の定義を考える:

template <class Operation> 
class binder1st : 
   public unary_function<typename Operation::second_argument_type, typename Operation::result_type> 
{ 
protected: 
   Operation op; 
   typename Operation::first_argument_type value; 
public: 
   binder1st(const Operation& x, const typename Operation::first_argument_type& y); 
   typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const; 
}; 

以下のようなよくある状況において何が起こっているのかを考える。 ファンクタが2番目の引数を参照としたとき、そして、 Operation::second_argument_type を参照として定義した場合、 operator() は参照として渡された引数への参照となるだろう。 そしてこれは違反である。解決法として、operator() を call_traits を使って修正する方法がある:

typename Operation::result_type operator()(typename call_traits<typename Operation::second_argument_type>::param_type x) const;

このように Operation::second_argument_type が参照型の場合、引数は "参照の参照" ではなく参照として引き渡される。

例 3 (make_pair の問題):

std::make_pair に配列名を一つ (あるいは両方に) 渡すと、テンプレート引数の型推論が引数を "T 型の配列への const参照"として推論する。 これは文字列リテラル (すなわちリテラルの配列) に対しても適用される。 結果として、ポインタのペアを返す代わりに配列のペアを返そうとする。 そして配列型は CopyConstructible(コピーコンストラクト可能) では無いため、コードのコンパイルに失敗する。 解決法として make_pair に渡す引数を明示的にポインタにキャストする方法がある。 しかし、call_traits はもっと良い方法 (すなわち自動的な方法) を提供する (そしてキャストが型チェック無しに行われるかもしれないのに対し、 この方法は安全で実にジェネリックなコードだ):

template <class T1, class T2>
std::pair<
   typename boost::call_traits<T1>::value_type, 
   typename boost::call_traits<T2>::value_type> 
      make_pair(const T1& t1, const T2& t2)
{
   return std::pair<
      typename boost::call_traits<T1>::value_type, 
      typename boost::call_traits<T2>::value_type>(t1, t2);
}

ここで、引数の型を配列と判断した場合、自動的にポインタに変換する。 型を決定するためのラッパー関数全てにおいて言えることだが、同様の現象は、 標準の binder や adapter でも発生する。 注意: make_pair 関数の引数に call_traits を用いないことで、テンプレー ト引数の型が決定する事を避けている。

例 4 (optimising fill):

小さな組込型を関数引数に渡している場合、call_traits は最適化される だろう。これは主に、呼び出された関数がループを持ち、引数がそのループ内 で使用されている場合に効果を発揮する。 以下の例 (fill_example.cpp) では、std::fill が最適化される二つの場合を示している。もし、引数の型が 1byte の組込型であれば std::memset が使用され、そうでなければ C++ の形 式通りの実装が利用される。しかし後者の場合でも、引数にcall_traits を使 用すれば最適化が行われる:

namespace detail{

template <bool opt>
struct filler
{
   template <typename I, typename T>
   static void do_fill(I first, I last, typename boost::call_traits<T>::param_type val);
   {
      while(first != last)
      {
         *first = val;
         ++first;
      }
   }
};

template <>
struct filler<true>
{
   template <typename I, typename T>
   static void do_fill(I first, I last, T val)
   {
      memset(first, val, last-first);
   }
};

}

template <class I, class T>
inline void fill(I first, I last, const T& val)
{
   enum{ can_opt = boost::is_pointer<I>::value
                   && boost::is_arithmetic<T>::value
                   && (sizeof(T) == 1) };
   typedef detail::filler<can_opt> filler_t;
   filler_t::template do_fill<I,T>(first, last, val);
}

補足: なぜなら、小さな組込型の最適化が行われると、値の渡し方が "const T&" ではなく "T const" となり、コンパイ ラは、引数の値が不変かつ非参照であると言える。 この情報を元に、コンパイラは引数をレジスタにキャッシュすることができ、 ループをアンロールしたり、(サポートされているならば)並列処理命令を使う ことが出来る。 実際の所、あなたのコンパイラがどのくらい最適化してくれるのか -- 我々は 実際にいくつかのベンチマークソフトで、このような幾つかのケースについて 計測を行うことが出来た。

fill の引数に call_traits を使っていないことに注意しなくてはならな い。使ってしまうと、テンプレート引数の型推論が妨げられてしまう。 そして fill は "thin wrapper"として機能し ("thin wrapper" とは、テンプレート引数を解決するだけの機能を持つラッパの 事 )、コンパイラは全ての fill 関数の呼び出しを、引数に call_traits を 渡す filler<>::do_fill に置き換えるような最適化を行うだろう。

原理

以下のノートで、 call_traits の作成にあたり合理的な選択があったこと を簡潔に説明する。

全てのユーザー定義型については既存の方法に従うため、コメン トしない。

小さな組込型("標準"では "fundamental types" と表記[3.9.1]) について の既存のプラクティスとの違いは param_type typedef のみ。 この場合 "T const" は既存の方法と互換性があるが、いくつかの 場合パフォーマンスが向上する (see Example 4)。どの ような場合においても既存の方法より遅くなることはない。

ポインタ型は小さな組込型の扱いに従う。

For reference types the rational follows Example 2 - references to references are not allowed, so the call_traits members must be defined such that these problems do not occur. There is a proposal to modify the language such that "a reference to a reference is a reference" (issue #106, submitted by Bjarne Stroustrup), call_traits<T>::value_type and call_traits<T>::param_type both provide the same effect as that proposal, without the need for a language change (in other words it's a workaround).

For array types, a function that takes an array as an argument will degrade the array type to a pointer type: this means that the type of the actual parameter is different from its declared type, something that can cause endless problems in template code that relies on the declared type of a parameter. For example:

template <class T>
struct A
{
   void foo(T t);
};

In this case if we instantiate A<int[2]> then the declared type of the parameter passed to member function foo is int[2], but it's actual type is const int*, if we try to use the type T within the function body, then there is a strong likelyhood that our code will not compile:

template <class T>
void A<T>::foo(T t)
{
   T dup(t); // doesn't compile for case that T is an array.
}

By using call_traits the degradation from array to pointer is explicit, and the type of the parameter is the same as it's declared type:

template <class T>
struct A
{
   void foo(typename call_traits<T>::value_type t);
};

template <class T>
void A<T>::foo(typename call_traits<T>::value_type t)
{
   typename call_traits<T>::value_type dup(t); // OK even if T is an array type.
}

For value_type (return by value), again only a pointer may be returned, not a copy of the whole array, and again call_traits makes the degradation explicit. The value_type member is useful whenever an array must be explicitly degraded to a pointer - Example 3 provides the test case (Footnote: the array specialisation for call_traits is the least well understood of all the call_traits specialisations, if the given semantics cause specific problems for you, or don't solve a particular array-related problem, then I would be interested to hear about it. Most people though will probably never need to use this specialisation).


Revised 01 September 2000

ゥ Copyright boost.org 2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.

Based on contributions by Steve Cleary, Beman Dawes, Howard Hinnant and John Maddock.

Maintained by John Maddock, the latest version of this file can be found at www.boost.org, and the boost discussion list at www.yahoogroups.com/list/boost.

.


Japanese Translation Copyright © 2003 Shimizukawa Takayuki

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