type_traits を実装する (2)
つい先日まで、is_constructible は SFINAE を使って以下のように実装できるものだと思っていました。
※この記事では void や array of unknown bound は考慮しません。
template <class T, class ...Args> struct is_constructible { private: template <class U, class = int> struct test : std::false_type {}; template <class U> struct test<U, decltype(U(std::declval<Args>()...), 0)> : std::true_type {}; public: static bool const value = test<T>::value; }; template <class T, class Arg> struct is_constructible<T, Arg> { private: template <class U, class = int> struct test : std::false_type {}; template <class U> struct test<U, decltype(static_cast<U>(std::declval<Arg>()), 0)> : std::true_type {}; public: static bool const value = test<T>::value; };
GCC 4.6.0 でもこのような実装になっています (void に対応していないところまで同じです)。しかし、この実装では次のようなコードでコンパイルエラーになってしまいます。
struct s { private: s(); }; static_assert(!is_constructible<s>::value, "error"); // error: s::s() is private
これは、access error が SFINAE failure にならないことによります。
規格は、access error を生じる場合であっても、is_constructible が false を返すことを要求しています。したがって、その実装にはコンパイラのサポートが必要になります。is_convertible も同様です。
※これ以外にも、is_constructible
しかし、is_copy_constructible は正しい is_constructible がなくても実装できます。
template <class T> struct is_copy_constructible { template <class U> struct wrapper { U u; }; template <class U, class = int> struct test : std::false_type {}; template <class U> struct test<U, decltype(wrapper<U>(std::declval<wrapper<U> const &>()), 0)> : std::true_type {}; static bool const value = test<T>::value; }; struct s { private: s(s const &); }; static_assert(!is_copy_constructible<s>::value, "error"); // passed
T がコピー不可であるとき (コピーコンストラクタが private である場合など)、暗黙に宣言される wrapper