C++では typename std::is_convertible<decltype(x), bool>::value が false の場合であっても if(x){} と書ける場合が存在する. どのような場合か答えよ
— 銀天すばる@野良C++er (@SubaruG) March 30, 2013
@ezoeryou x がスマートポインタ等で explicit operator bool が定義されている場合, is_convertible では false になった筈です
— 銀天すばる@野良C++er (@SubaruG) March 30, 2013
これをちょっと考えてみたのだが、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:
Post a Comment