型特性を調べる
ヘッダファイル<type_traits>をインクルードすると、標準ライブラリによって型特性を調べることができるようになる。std::is_sameとかstd::is_constructibleとか。 大体のものはそれだけで事足りるが、今回はある型がSwappableかどうか確認したくなったので実装してみようと思った。
is_swappableについて
Swappableとは
参照:http://en.cppreference.com/w/cpp/concept/Swappable
本当はC++規格書とか引用するべきだろうけど、ここではオンラインのリファレンスサイトで…
ざっくりというと、次を満たせば良いみたい(著しく正確性を欠く表現)。
- 型Tのオブジェクトtと型Uのオブジェクトuにおいて、文脈上"swap(t, u)"と"swap(u, t)"が有効であること
- swapしたら、tとuの値が入れ替わってること
1を調べる
コード以上に正確に伝えられる言語を習得していないので、コードから。
// http://melpon.org/wandbox/permlink/k0L4sbTFBEuqK7vm #include<cstddef> #include<type_traits> #include<utility> namespace skystar0227 { namespace implement { template<typename T> using Test = decltype(T::test(nullptr)); namespace swappable { using std::swap; template<typename T0, typename T1> using CallTest = decltype ( swap(std::declval<T0>(), std::declval<T1>()) , swap(std::declval<T1>(), std::declval<T0>()) ); } // namespace swappable template<typename T0, typename T1> struct Swappable { template < typename T0_ = typename std::add_lvalue_reference<T0>::type , typename T1_ = typename std::add_lvalue_reference<T1>::type , typename = swappable::CallTest<T0_, T1_> > static auto test(std::nullptr_t)->std::true_type; static auto test(...)->std::false_type; }; } // namespace implement template<typename T0, typename T1 = T0> using is_swappable = implement::Test<implement::Swappable<T0, T1>>; } // namespace skystar0227 class Foo{}; class Bar: Foo{}; namespace std { template<> void swap<Foo>(Foo&, Foo&) noexcept = delete; } // namespace std class Hoge{}; namespace hoge { class Hoge{}; void swap(Hoge&, Hoge&) = delete; } // namespace hoge #include<iostream> auto main()->int { using skystar0227::is_swappable; /*0*/std::cout << is_swappable<void>::value << std::endl; /*1*/std::cout << is_swappable<int>::value << std::endl; /*0*/std::cout << is_swappable<int const>::value << std::endl; /*1*/std::cout << is_swappable<int&>::value << std::endl; /*1*/std::cout << is_swappable<int&&>::value << std::endl; /*0*/std::cout << is_swappable<Foo>::value << std::endl; /*1*/std::cout << is_swappable<Bar>::value << std::endl; /*1*/std::cout << is_swappable<Hoge>::value << std::endl; /*0*/std::cout << is_swappable<hoge::Hoge>::value << std::endl; return 0; }
次のis_swappableが実体。実装はimplement::Swappableへ。最終的にstd::false_typeかstd::true_typeかに落ち着く。
template<typename T0, typename T1 = T0> using is_swappable = implement::Test<implement::Swappable<T0, T1>>;
implement::Testは、SFINAEを利用して定義される静的メンバ関数testの戻り値型を取得する。これがテスト結果となる。nullptrを渡すのは、多重定義を曖昧にしないため。
template<typename T> using Test = decltype(T::test(nullptr));
implement::Swappableで、静的メンバ関数testを定義する。デフォルトテンプレート引数を利用したC++11的なSFINAEを採用。参考にしたページを失念した…
template<typename T0, typename T1> struct Swappable { template < typename T0_ = typename std::add_lvalue_reference<T0>::type , typename T1_ = typename std::add_lvalue_reference<T1>::type , typename = swappable::CallTest<T0_, T1_> > static auto test(std::nullptr_t)->std::true_type; static auto test(...)->std::false_type; };
using std::swap;を書きたいがためにnamespace swappableを別途定義して呼出テスト。
namespace swappable { using std::swap; template<typename T0, typename T1> using CallTest = decltype ( swap(std::declval<T0>(), std::declval<T1>()) , swap(std::declval<T1>(), std::declval<T0>()) ); } // namespace swappable
main関数は、ひと通り意図した挙動を示すか確認できるコードを書いてみた。
2を調べる
無理じゃね?コンパイル時に調べる方法はちょいと思いつかない。
結論:妥協しよう
ということで、不完全だが、is_swappableを実装してみた。少なくともswap関数を呼び出せるかの確認には使えるさ。私は満足だ。
0 件のコメント:
コメントを投稿