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 のようなコードがコンパイルエラーになりますが (GCC 4.6.0)、理由は不明です。
しかし、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 のコピーコンストラクタは delete される (12.8 p12) ことを利用しています。アクセス権の問題は delete の問題に変換され、SFINAE の対象となるわけです。