2008-01-27

テンプレート関数で、constを引数に使った場合

 某C++スレで、こんな話がでた。アクセス規制がかかっているので、ここにメモと、正しい答えを書いておく。  質問者は、次のコードの出力に納得ができないでいる。

template < typename T > void f( T ) { std::cout << boost::is_const< T >::value ; } int main() { int x = 0 ; f( x ) ; // 0が出力される int const y = 0 ; f( y ) ; // 0が出力される f<int const>( 0 ) ; // 1が出力される }

 なるほど、確かに、一見すると不思議な結果だ。二つ目の関数呼び出しの引数の型は、int constではないのだろうか。その疑問、もっともだ。ちなみに、このコードは、規格では、001と出力されるのが正しい。  いったいどういう仕組みなのか。まず、関数の引数における、関数の引数のtop-level qualifierは、関数の型に影響を与えない。つまり、constとvolatileで修飾しても、関数の型として見た場合、引数は修飾されていないのだ。

int main() { typedef void func_type( int const volatile) ; std::cout << typeid( func_type ).name() << std::endl ; std::cout << typeid ( boost::function_traits< func_type >::arg1_type ).name() << std::endl ; }

 一行目は、void (int const volatile)という型の関数をtypedefしている。  二行目は、人間が読める形で型を出力する。これは処理系依存だが、VC9の場合、void __cdecl(int) と出力される。__cdeclとはVC特有の呼び出し規約のことで、ここでは関係がない。注目すべきは引数の型だ。たんなるint型になっている。  三行目は、Boostのfunction_traitsというメタ関数を使い、引数の型を調べている。これも、int と出力される。もちろん、function_traitsがcv_qualifierを取り除いているわけではない。ただ規格通りなだけだ。  ここまで分かったら、同じように、f<int> と、f<int const> とを、typeidで比較してみるとよい。どちらも同じ文字列を出力するはずだ。(しかし、ひょっとしたら、ソースコードでの型を出力する処理系があるかもしれない。規格では、関数の型は修飾を省くので、それは正しい型ではないと思うのだけれど)

 よろしい。それは分かった。しかし、もうひとつ解せないことがある。それは、テンプレート引数を明示的に指定した場合は、なぜか、関数内では、引数の型は、cv_qualifierがついているということだ。コレはなぜか。それには、C++の規格にあたらなければならない。14.8.2の3に、次のように書いてある。

[Note: A top-level qualifier in a function parameter declaration does not affect the function type but still affects the type of the function parameter variable within the function. —end note]

 そして、例として示されているコードは、まさに今論じている問題のことだ。つまり、関数の型としては、cv_qualifierがつかないが、その関数内では、引数の型には、cv_qualifierがつく。以上。

 追記  さらに、なぜ、int constな型を引数として渡しているのに、int型にdeductionが働くのかという疑問に対しては、14.8.2.1に、次のように書いてある。

1 Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. 2 If P is not a reference type: (中略) — If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction. If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction.

 つまり、トップレベルのcv_qualifierは無視されるということだ。

No comments: