型特性を調べる
ヘッダファイル<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 件のコメント:
コメントを投稿