Boost.ConceptTraitsのis_assignable

Boost.ConceptTraits(没になったのかどうか知らないけど、正式入りしてないライブラリ)
にis_assignableがあったので、抜き出して試してみました。(VC++8.0)


ドキュメント

ダウンロード先

#include <boost/static_assert.hpp>

#include <boost/type_traits.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>

namespace shand { namespace assignable_detail {

template <class T>
struct remove_r : boost::remove_reference<T> {};

template <class T>
struct remove_cvr : boost::remove_cv<typename boost::remove_reference<T>::type> {};

template <class T>
struct remove_cvpr :
    boost::remove_cv<
        typename boost::remove_pointer<
            typename remove_r<T>::type
        >::type
    > {};


template<class T>
struct is_not_const :
    boost::mpl::not_<
        boost::is_const<typename remove_r<T>::type>
    > {};


template<class T,class U>
struct is_object_and_not_array_or_member_function_pointer_and_same_type :
    boost::mpl::and_<
        boost::mpl::or_<
            boost::mpl::and_<
                boost::is_object<typename remove_r<T>::type>,
                boost::mpl::not_<
                    boost::is_array<typename remove_r<T>::type>
                >
            >,
            boost::is_member_function_pointer<typename remove_r<T>::type>
        >,
        boost::is_same<
            typename remove_cvr<T>::type,
            typename remove_cvr<U>::type
        >
    > {};


template<class T>
struct is_arithmetic_or_enum :
    boost::mpl::or_<boost::is_arithmetic<T>, boost::is_enum<T> > {};


template<class T,class U>
struct is_arithmetic_and_arithmetic_or_enum :
    boost::mpl::and_<
        boost::is_arithmetic<typename remove_r<T>::type>,
        is_arithmetic_or_enum<typename remove_r<U>::type>
    > {};


template<class T>
struct is_object_pointer :
    boost::mpl::and_<
        boost::is_pointer<T>,
        boost::mpl::not_<
            boost::is_function<typename boost::remove_pointer<T>::type>
        >
    > {};

template<class T,class U>
struct is_compatible_object_pointers :
    boost::mpl::and_<
        is_object_pointer<typename remove_r<U>::type>,
        boost::mpl::or_<
            boost::is_same<typename remove_cvr<T>::type, void*>,
            boost::is_base_and_derived<
                typename remove_cvpr<T>::type,
                typename remove_cvpr<U>::type
            >
        >
    > {};


template<class T,class U>
struct is_compatible_pointers :
    boost::mpl::and_<
        boost::is_pointer<typename boost::remove_reference<T>::type>,
        boost::mpl::or_<
            boost::is_same<
                boost::remove_cv<typename remove_r<T>::type>,
                boost::remove_cv<typename remove_r<U>::type>
            >,
            is_compatible_object_pointers<T,U>
        >
    > {};


template<class T>  
struct is_class_or_union_ :
    boost::mpl::or_<
        boost::is_class<T>,
        boost::is_union<T>
    > {};


}} // namespace shand::detail


namespace shand {

template<class T,class U = T>
struct is_assignable :
    boost::mpl::and_<
        assignable_detail::is_not_const<typename boost::remove_reference<T>::type>,
        boost::mpl::or_<
            assignable_detail::is_object_and_not_array_or_member_function_pointer_and_same_type<T,U>,
            assignable_detail::is_arithmetic_and_arithmetic_or_enum<T,U>,
            assignable_detail::is_compatible_pointers<T,U>,
            assignable_detail::is_class_or_union_<typename boost::remove_reference<T>::type>
        >
    > {};

} // namespace shand

struct A {};

struct B {
    int& r;
};

struct C {
private:
    C& operator=(const C&); // 宣言して定義しない
};

int main()
{
    BOOST_STATIC_ASSERT(shand::is_assignable<int>::value);  // OK
    BOOST_STATIC_ASSERT(shand::is_assignable<int*>::value); // OK
    BOOST_STATIC_ASSERT(shand::is_assignable<A>::value);    // OK
    BOOST_STATIC_ASSERT(shand::is_assignable<B>::value);    // OK : エラーになってほしい
    BOOST_STATIC_ASSERT(shand::is_assignable<C>::value);    // OK : リンク時なのでこれはどちらにしろ無理

//  BOOST_STATIC_ASSERT(shand::is_assignable<const int>::value); // NG
}

というか、非constのクラスなら通るようになってるから無理か。
これ書いてる間に別な案を考えたので明日あたり試してみよう。



追記:
BOOST_FUSION_ADAPT_STRUCTで、クラスメンバを型リストに変換し、
参照とconstのメンバを探す、というのをやろうと思ったけど
BOOST_FUSION_ADAPT_STRUCTの引数でメンバの型を書かないといけないのと、
参照を指定することができなかったので断念。