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