C++1z void_t

C++1zから、SFINAEによる「型に対して特性の操作ができるか」を判定するメタ関数の定義を容易にするために、void_tというパラメータで任意の数の型を受け取ってなにもせずvoidを返す型が定義されます。

// <type_traits>
namespace std {
  template <class...>
  using void_t = void;
}

私の著書『C++テンプレートテクニック 第2版』に掲載しているコードで、これまでのメタ関数定義と、void_tを使用したメタ関数定義を比べてみます。

型Tがiterator型を持っているか判定する

これまでの書き方でhas_iteratorメタ関数を定義する:

#include <type_traits>

struct has_iterator_impl {
    template <class T>
    static std::true_type check(typename T::iterator*);

    template <class T>
    static std::false_type check(...);
};

template <class T>
struct has_iterator :
    decltype(has_iterator_impl::check<T>(nullptr)) {};

#include <vector>
int main()
{
    static_assert(
        has_iterator<std::vector<int>>::value,
        "vectorはiterator型を持っている");

    static_assert(
        !has_iterator<int>::value,
        "intはiterator型を持っていない");
}

void_tを使用した場合:

#include <type_traits>

template <class, class = void>
struct has_iterator
    : std::false_type {};

template <class T>
struct has_iterator<T, std::void_t<typename T::iterator>>
    : std::true_type {};

#include <vector>
int main()
{
    static_assert(
        has_iterator<std::vector<int>>::value,
        "vectorはiterator型を持っている");

    static_assert(
        !has_iterator<int>::value,
        "intはiterator型を持っていない");
}

だいぶ短くなりました。

型Tが代入可能か判定する

これまでの書き方でis_assignableを定義する:

#include <type_traits>
#include <utility>

struct is_assignable_impl {
    template <class T>
    static auto check(T*) -> decltype(
        std::declval<T&>() = std::declval<const T&>(),
        std::true_type());

    template <class T>
    static auto check(...) -> std::false_type;
};

template <class T>
struct is_assignable
    : decltype(is_assignable_impl::check<T>(nullptr)) {};

struct A {
    A& operator=(const A&) = delete;
};

struct B {};

int main()
{
    static_assert(!is_assignable<A>::value, "Aは代入不可");
    static_assert( is_assignable<B>::value, "Bは代入可能");
}

void_tを使用した場合:

#include <type_traits>
#include <utility>

template <class, class = void>
struct is_assignable : std::false_type {};

template <class T>
struct is_assignable<
    T,
    std::void_t<decltype(std::declval<T&>() = std::declval<const T&>())>
> : std::true_type {};

struct A {
    A& operator=(const A&) = delete;
};

struct B {};

int main()
{
    static_assert(!is_assignable<A>::value, "Aは代入不可");
    static_assert( is_assignable<B>::value, "Bは代入可能");
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。