2012-06-04

インスタンス化による副作用としてのインスタンス化はimmediate contextではない

まさかそんなことはないだろう。それなら以下のコードはコンパイルエラーになってしまうはずではないかと思って試したところ、本当にコンパイルエラーになってしまった。なるほど、たしかに正しい。

template < typename T >
struct X
{
    using type = T ; 
} ;

template < >
struct X<int> { } ;

template < typename T >
struct Y
{
    using type = typename X<T>::type ;
} ;

template < typename T, typename Dummy = typename X<T>::type >
void f(T) {}
void f( short ) { }

template < typename T, typename Dummy = typename Y<T>::type >
void g(T) {}
void g( short ) { }

int main()
{
    f( 0 ) ; // OK
    g( 0 ) ; // Can be Error
}

関数fは、Dummyのsubstitutionに失敗するので、通常の関数が選ばれる。関数gは、クラステンプレートをひとつ挟んでいる。こうすると、クラステンプレートYのインスタンス化は、クラステンプレートXのインスタンス化をもたらす。このインスタンス化は、immediate contextではないので、SFINAEには引っかからず、ill-formedとなる。

なるほど、これは気が付かなかった。

問題は、なんでこうなっているのかということだ。しかも、これがエラーかどうかは、規格上は定められていないのだ。なぜならば、

The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc.Such side effects are not in the “immediate context” and can result in the program being ill-formed.

§ 14.8.2 paragraph 8 note.

"can result in the program being ill-formed"となっている。つまり、規格上、エラーでなくても構わないのだ。

SFINAE周りは、だいぶコンパイラーごとの差異が激しかった部分である。C++03までは、ホワイトリスト方式で、エラーとはならない場合を列挙していた。C++11では、これを完全するため、従来のホワイトリストを残しつつ、文字通り、substitutio failure is not an errorを実現するべく、その他の場合もエラーではないとした。

しかし、まさかこんな落とし穴が残ってい用途は思わなかった。これは説明に窮する。もちろん、定義はしっかりしているが、なぜこんなルールになっているのかという疑問が沸く。なぜだ。

3 comments:

Anonymous said...

> なぜだ。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html
via http://www.google.co.jp/search?q=site%3Awww.open-std.org%2FJTC1%2FSC22%2FWG21+%22can+result+in+the+program+being+ill-formed%22
> "Speculative compilation" sounds like an easy way out, but in practice compilers can't do that.
などなど。

江添亮 said...

おお、読んでみます。
paperの山からどうやって探しだそうか、
いっそのこと、人に聞くべきかと思案していたところです。

江添亮 said...

まあ、予想通り実装上の都合かな。
完全に副作用に属するかどうかを判定するのは難しいという事か。