コンセプトの厳格さと面倒くささを確かめるために、実用的なコードをスクラッチから書いてみた。一時間ほどかかった。ConceptGCCのエラーメッセージは不親切すぎる。
auto concept ValueTypeConcept< typename T >
{
T::T() ;
T::T( T const & ) ;
T::~T() ;
T & operator +=( T &, T const & ) ;
}
auto concept IteratorConcept< typename T >
{
T::T() ;
T::T( T const & ) ;
T::~T() ;
typename value_type = T::value_type ;
T & operator ++ (T & ) ;
value_type operator * ( T & ) ;
bool operator == ( T const &, T const & ) ;
bool operator != ( T const &, T const & ) ;
}
template < typename Iterator >
requires IteratorConcept< Iterator >
&& ValueTypeConcept< Iterator::value_type >
typename Iterator::value_type
accumlate( Iterator first, Iterator last )
{
if ( first == last )
return typename Iterator::value_type() ;
typename Iterator::value_type ret( *first ) ;
++first ;
while( first != last )
{
ret += *first ;
++first ;
}
return ret ;
}
なぜこの程度のコードを書くのに時間がかかったかと言うと、コンセプトの記述を、一箇所間違えてしまったからだ。
auto concept IteratorConcept< typename T >
{
typename value_type = T::value_type ;
value_type operator ++ (T & ) ;
}
こんなミスぐらいすぐに発見できると思うかもしれないが、まさかこんな間違いをするはずが無いという思い込みから、まったく発見できなかった。また、ConceptGCCのエラーメッセージもひどい。STLのint型の要素のvectorのイテレータを渡したら、
test.cpp: In function 'int main()':
test.cpp:72: error: no matching function for call to 'accumlate(__gnu_cxx::__normal_iterator<int*,std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >)'
これだけしかエラーを出力しない。まず、最低限どのコンセプトを満たしていないかぐらい教えてくれよ。それから、あるコンセプトのどの要求を満たしていないのか、ということも教えて欲しい。もしConceptGCCが、
「あんさんあんさん、このコード間違ってまんがな。IteratorConceptっちゅーコンセプトのな、value_type operator ++ (T & ) ; ちゅー要求を、std::vector<int>::iteratorは満たしてへんねやで。もひとつ云うておくとやな、たしかにoperator ++はありまんねん。せやけど戻り値をvalue_typeに変換可能なものはおまへん。そこんとこ気ぃつけて、まひとつよろしく頼んます」
と言ってくれたならば、どんなに楽なことか。このエラーメッセージには、現在のConceptGCCにできないことが三つある。まず一つ目は、どのコンセプトにマッチしていないのか知らせることだ。ConceptGCCは、ちょっと複雑なコードになってくると、とたんにこれができなくなる。二つ目は、typedefされた型名を用いるということだ。libstdc++の内部的な実装の型名を使われても、わけが分からない。MSVCを見習って欲しいものだ。またその際には、テンプレートパラメータのデフォルト引数は省略してくれれば、なおよい。三つ目は、戻り値や引数の型が違う場合に、警告してくれることだ。
No comments:
Post a Comment