これは「Two Phase Name Look up」というテンプレートに関する名前探索によるものですね。規格上、コンパイラはテンプレートは定義時点と、インスタンス化される時点の2回に分けてテンプレートを解釈します。(VCはおそらくこれを無視してすべて(2)の時点で名前解決をしているから限定子がなくても通ってしまうのでしょう。しかしこれは規格違反です。)
(1)テンプレート定義時点
テンプレート定義時点では、
テンプレート引数に依存しない部分について解釈します。具体的には、; の漏れや、キーワードのスペルミスとかですね。また、テンプレート引数に依存しない名前について、その場で解決してしまいます。(※テンプレート自身と、テンプレート引数についてはこの時点で解決される。)
(2)インスタンス化される時点
インスタンス化される時点では、(1)で解決できなかった部分。つまり
テンプレート引数に依存する部分について解釈します。よって、テンプレート引数に依存する名前については、この時点で解決されることになります。
では、どのようなときにtypename限定子やtemplate限定子が必要になるのかというと、基本的には、その文脈においてテンプレートパラメータに依存している型名やメンバテンプレートが出てきたときです。たとえば、
コード:
//(A)
template<typename T,int V>
struct X{
void func(){
T::u<V> val; //(T::u < V) > val; と解釈される
}
}
コード:
//(B)
template<typename T,int V>
struct X{
void func(){
T::template u<V> val; //今度はT::uをテンプレートとして解釈する。
}
}
domさんが提示したコードではtemplate限定子が必要ないのは、choose_range_list::bind自体はrange_list_generatorのテンプレートパラメータに依存した名前じゃないからです。(ただし、choose_range_list::bind<T,NumRanges>::type はテンプレートパラメータに思いっきり依存しているのでtypenameが必要ですけどね。)
コード:
struct choose_range_list{
template <typename T, std::size_t NumRanges>
struct bind {
typedef boost::array<T,NumRanges> type;
};
};
template <typename T, std::size_t NumRanges>
struct range_list_generator {
public:
typedef typename choose_range_list::template bind<T,NumRanges>::type type;
// ↑これです
};