2013-04-02

Implicitly converted vs contextually converted to bool

これをちょっと考えてみたのだが、Concept Liteで問題になりはしないだろうか。例えば以下のようなコード。

template < typename T >
void f( T & e )
{
    if ( e ) ; 
}

これをConcept Liteで以下のように書くと誤りである。

template < typename T >
requires std::is_convertible< T, bool >::value
void f( T & e )
{
    if ( e ) ;
}

以下のように書いても誤りである。


template < typename T >
constexpr bool is_implicitly_convertible_to_bool()
{
    return requires ( T e )
    {
        bool = { e } ;
    } ;
}

template < typename T >
requires is_implicitly_convertible_to_bool<T>()
void f( T & e )
{
    if ( e ) ;
}

これはどちらも、Tをboolへimplicitly convertibleかどうかを調べているためである。

このため、explicit operator bool()をメンバーに持つ型(e.g. std::unique_ptr)を、誤って弾いてしまう。

ifのような文脈では、contextually convertible to boolとも言うべき用語がでてくる。これは、宣言、bool t(e);が成り立つ式eのことである。

つまり、以下のように書くのが正しい。

template < typename T >
requires std::is_constructible< bool, T >::value
void f( T & e )
{
    if ( e ) ;
}

あるいは以下のように書く。

template < typename T >
constexpr bool is_contextually_convertible_to_bool()
{
    return requires ( T e )
    {
        { bool(e) } ;
    } ;
}

template < typename T >
requires is_contextually_convertible_to_bool<T>()
void f( T & e )
{
    if ( e ) ;
}

たかがifやforやwhileのごとき基本的なコア言語機能を使うテンプレートのConceptチェックに、implicitly convertedとcontextually converted to boolの違いを完璧に理解していなければならないのだろうか。

とはいえ、特にいい解決方法も思いつかないので、Concept利用者は当然、この程度のことは理解しているべきだと思う。しかし、これは分かっていても間違えそうだ。

ただし、explicit operator bool()を使うという事は、もともと結構面倒でもある。たとえば、

struct X
{
    explicit operator bool() ;
} ;

void f()
{
    X x ;
    bool good1(x) ; // OK
    bool bad = x ; // エラー
    bool good2 = bool(x) ; // OK

    if( x ) ; // OK
    if( x == true ) ; // エラー
    if( bool(x) == true ) ; // OK
}

こういうことになってしまうのだから。

No comments: