Rokiのチラ裏

学生による学習のログ

型がTrivially CopyableだけではなくCopyableであるかを判定するメタ関数

is_trivially_copyableはtriviallyコピーでなければコピー可能だとしても許容されない。トライバルでなくてもコピー可能な型を判定するメタ関数はそういえば見たことが無かったので、作ってみた。

template<class _Tp,_Tp&(_Tp::*)(const _Tp&)>
struct copy_signature{
    using type=_Tp;
};
template<class _Tp,class=_Tp>
struct copy_f_deleted:std::false_type{};
template<class _Tp>
struct copy_f_deleted<_Tp,typename copy_signature<_Tp,&_Tp::operator=>::type>:std::true_type{};
template<class _Tp>
constexpr bool is_copyable_v=copy_f_deleted<_Tp>::value;

int main()
{
    std::cout<<std::boolalpha<< is_copyable_v<std::unique_ptr<int>> << std::endl; // false
    std::cout<< is_copyable_v<std::vector<int>> << std::endl; // true
}

仕組みは極単純である。これを利用すれば、例えばBoost.Anyのような型に対してunique_ptrを持たせる事などが可能となる*1

template<class _Tp,_Tp&(_Tp::*)(const _Tp&)>
struct copy_signature{
    using type=_Tp;
};
template<class _Tp,class=_Tp>
struct copy_f_deleted:std::false_type{};
template<class _Tp>
struct copy_f_deleted<_Tp,typename copy_signature<_Tp,&_Tp::operator=>::type>:std::true_type{};
template<class _Tp>
constexpr bool is_copyable_v=copy_f_deleted<_Tp>::value;

struct Any final{
    struct Erasure{
        virtual ~Erasure()=default;
    };
    template<class _Tp>
    struct Erasure_:Erasure{
        explicit Erasure_(const _Tp& x)noexcept
            :data(x){}
        
        _Tp& get(){return data;}      
    private:
        _Tp data;
    };
    template<class _Tp>
    struct Erasure_nocopyable:Erasure{
        explicit Erasure_nocopyable(_Tp&& x)noexcept
            :data(std::move(x)){}
        
        _Tp&& get(){return std::move(data);}
    private:
        _Tp data;
    };

    template<class _Tp,
        std::enable_if_t<
            (!std::is_same<std::remove_reference_t<_Tp>,Any>{} && is_copyable_v<_Tp>) || std::is_pod<std::remove_reference_t<_Tp>>{},
        std::nullptr_t> =nullptr>
    Any(_Tp&& x)
        :ptr(new Erasure_<_Tp>(std::forward<_Tp>(x)))
    {}
    template<class _Tp,
        std::enable_if_t<
            (!std::is_same<std::remove_reference_t<_Tp>,Any>{} && !is_copyable_v<_Tp>) && !std::is_pod<std::remove_reference_t<_Tp>>{},
        std::nullptr_t> =nullptr>
    Any(_Tp&& x)
        :ptr(new Erasure_nocopyable<_Tp>(std::forward<_Tp>(x)))
    {}

    template<class _Tp,
        std::enable_if_t<
            is_copyable_v<_Tp> || std::is_pod<std::remove_reference_t<_Tp>>{},
        std::nullptr_t> =nullptr>
    _Tp get()
    {
        return std::static_pointer_cast<Erasure_<_Tp>>(ptr)->get();
    }
    template<class _Tp,
        std::enable_if_t<
            !is_copyable_v<_Tp> && !std::is_pod<std::remove_reference_t<_Tp>>{},
        std::nullptr_t> =nullptr>
    _Tp get()
    {
        return std::static_pointer_cast<Erasure_nocopyable<_Tp>>(ptr)->get();
    }
private:
    std::shared_ptr<Erasure> ptr;
};

int main()
{
    using namespace std::string_literals;

    Any a = 10;
    Any str = "hoge"s;
    Any ptr = std::make_unique<int>(42);

    std::cout<< a.get<int>() <<std::endl; // 10
    std::cout<< str.get<std::string>() <<std::endl; // hoge
    std::cout<< *ptr.get<std::unique_ptr<int>>() <<std::endl; // 42
}

NOTE:コピー禁止のオブジェクトは、getすると当然ながらムーブされるため、再度getを行うと未定義となる。

追記

上記Anyでは、POD型に対する許容を手で書いていたが、どうせならその機能も内包した方が親切である。ついでに、is_moveableも書いた。これで、POD(トライバルな)型もコピー可能として許容される。

struct X{
    X(X&&)=delete;
};

void pfn(bool b){std::cout<<std::boolalpha<<b<<std::endl;}

int main()
{
    pfn( is_copyable_v<std::unique_ptr<int>> ); // false 
    pfn( is_copyable_v<std::vector<int>> ); // true
    pfn( is_copyable_v<int> ); // true
    pfn( is_copyable_v<X> ); // false

    pfn( is_moveable_v<std::unique_ptr<int>> ); // true
    pfn( is_moveable_v<std::vector<int>> ); // true
    pfn( is_moveable_v<int> ); // true
    pfn ( is_moveable_v<X> ); // false
}

参照

*1:Boost.Anyにはonly moveableな型に対する対応はない