2016-01-07

yumetodoが本当に欲しかったもの

template引数として与えられた2つの型TypeFromとTypeToがあるとき、TypeFromからTypeToへの変換がintegral promotionsであるか判別し、そうであるならstd::true_type、違うならstd::false_typeを継承するクラスを作るにはどうかけばいいのか?

https://ask.fm/EzoeRyou/answers/134026904663

integral promotionsとは、C++規格の§4.5で規定されている。問題は、integral promotionsの内容を忠実に判定すると、charからshortへの変換はintegral promotionではないし、intからlongへの変換もintegral promotionではない。

それでもいいのであれば、実装は以下のようになる。

// check if from can be converted and represented to to.
template < typename from, typename to >
constexpr bool can_represent()
{
    return std::is_same< typename std::common_type< from, to >::type, to >{} ; 
}

template < typename from, typename to >
constexpr bool is_integral_promotion_impl()
{
// as per section 5.4 paragraph 6
    if ( std::is_same<from, bool>{} )
    {
        return std::is_same<to, int>{} ;
    }

// as per section 5.4 paragraph 3
    if (
            std::is_same<from, char16_t>{} ||
            std::is_same<from, char32_t>{} ||
            std::is_same<from, wchar_t>{} )
    {
        if ( can_represent<from, int>() )
            return std::is_same<to, int>{} ;
        else if ( can_represent< from, unsigned int>() )
            return std::is_same< to, unsigned int >{} ;
        else if ( can_represent< from, long int>() )
            return std::is_same< to, long int>{} ;
        else if ( can_represent< from, unsigned long int>() )
            return std::is_same< to, unsigned long int >{} ;
        else if ( can_represent< from, long long int >() )
            return std::is_same< to, long long int>{} ;
        else if ( can_represent< from, unsigned long long int>() )
            return std::is_same< to, unsigned long long int>{} ;
    }

// as per section 4.5 paragraph 3-4
    if ( std::is_enum<from>{} &&
        // requires lazy intantiation because underlying_type<T>::type is ill-formed for non enum T.
        std::is_same< to, typename std::conditional< std::is_enum<from>{}, std::underlying_type<from>, std::decay<void> >::type::type >{} ) 
    {
        return true ;
    }

// as per section 4.5 paragraph 1
    if ( !std::is_integral<from>{} ||
        !std::is_integral<to>{} )
    {
        return false ;
    }

    if ( std::is_same< to, int >{} &&
         can_represent<from, int>() )
    {
        return true ;
    }

    if ( std::is_same< to, unsigned int >{} &&
         can_represent<from, unsigned int>() )
    {
        return true ;
    }
         

    return false ;
}


template < typename from, typename to >
struct is_integral_promotion
    : std::integral_constant<bool, is_integral_promotion_impl<from, to>() >
{ } ;

ただ、fromがtoに変換できてかつ精度を落とすことなく完全に表現できる程度であれば、

template < typename from, typename to >
struct is_representable
    : std::integral_constant<bool, can_represent<from, to>() >
{ } ;

これぐらいでもいいのではないか。

しかし、constexprがあるのでこの手の処理は書きやすくなったと思ったら、std::underlying_typeのような問題がある。これを簡単に賭けるようにするため、constexpr_ifの導入が必要だ。

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

1 comment:

yumetodo said...

うわ、そうか、std::common_typeってこういう使い方できるのか。

ありがとうございます。