2013-05-05

clangのローカルクラスをテンプレート実引数に明示的に渡した場合のバグについて

以下のコードがclangでコンパイルエラーになる。

template <class T>
void g()
{
    T x(0);
}
 
template <class T>
void f()
{
    struct local {
        local(int) {}
    };
 
    g<local>();
}
 
int main()
{
    f<void>();
}

エラーの内容は、g<local>で、適合するlocalのコンストラクターが見つからないというものだ。たしかにlocalにはint型をひとつ取るコンストラクターがあるというのに。

どうやらこれは、テンプレート(関数テンプレートやクラステンプレートのメンバー関数)内のローカルクラスを明示的テンプレート実引数指定すると、ローカルクラスのユーザー定義コンストラクターのlookupが失敗するというバグのようだ。

template < typename T >
struct S
{
    T t ;
    S() : t(0) { }
} ;

// local class defined inside template difinition.
template < typename T >
void f()
{
    struct local { local( int ) { } } ;

    S<local> s ;
}

// local class defined inside non-template difiniton.
void g()
{
    struct local { local( int ) { } } ;

    S<local> s ;
}


int main()
{
    f<void>() ; // error: no matching constructor for initialization of 'local'
    g() ; // OK, lookup works
}

テンプレート定義内で定義されていないローカルの場合は起こらない。

関数テンプレートの場合、明示的テンプレート実引数指定を使った場合だけ、この問題に引っかかる。

template < typename T >
void f( )
{
    T x(0) ;
}

template < typename T >
void call_f_template()
{
    struct local { local(int) {} } ;
    f<local>() ;
}

void call_f()
{
    struct local { local(int) {} } ;
    f<local>() ;
}

int main() { call_f_template<void>() ; // error: no matching constructor for initialization of 'local' call_f() ; }

Argument Deductionが使える文脈ではエラーにならない。そのような文脈では、たとえexplicit template argument specificationを使ったとしても問題なく動く。

template < typename T >
void f( T const & )
{
    T x(0) ;
}

template < typename T >
void call_f_template()
{
    struct local { local(int) {} } ;
    local obj(0) ;
    f( obj ) ; // OK
    f<local>( obj ) ; // Also OK
}

int main()
{
    call_f_template<void>() ;
}

実に不思議なバグだ。はじめてclangのC++11フロントエンドのバグを目にした。

というわけでバグ報告してみた。

Bug 15911 – User defined constructor lookup fails on local class defined inside template difinition and passed to another template as an template argument

追記:すでに重複報告だった。しかも2011年から既知のバグだ。当時はコンパイラーがクラッシュするバグだったらしい。

Bug 9685 – Crash on using local struct in function template as template parameter

No comments: