メタ関数コールバックを使おう


初めに

SFINAEを使ってメタ関数を書く際、いろいろな方法がありますがメタ関数コールバックという技法を使った書き方を推したいのでまとめます

これまでの書き方

例として任意の型がfunc()をもつかどうかチェックするメタ関数

初期の書き方

関数のオーバーロードと優先順位を利用した方法

SFINAE1
template<class T>
struct HasFunc
{
private:
    template<class U>
    static auto check()->decltype(std::declval<U>().func(), std::true_type{});
    template<class>
    static std::false_type check(...);
public:

    static constexpr bool value = decltype(check<T>())::value;

};

void_tを使った書き方

任意の型が型引数にあるときvoidを返すvoid_tを使い、template特殊化する方法

template<class...>
using void_t = void;
SFINAE2
template<class T,class = void>
struct HasFunc:std::false_type
{};
template<class T>
struct HasFunc < T, std::void_t<decltype(std::declval<T>().func())> > :std::true_type
{};

メタ関数コールバックを使ったこれからの書き方

テンプレートテンプレート引数を利用し、std::void_tの方法をさらにジェネリックにした方法

namespace detail
{
    template<class AlwaysVoid, template<class...>class Op, class ...Args>
    struct is_detected_impl :std::false_type
    {};
    template<template<class...>class Op, class ...Args>
    struct is_detected_impl<std::void_t<Op<Args...>>, Op, Args...> :std::true_type
    {};
}
//こいつを使う
template<template<class...>class Op, class ...Args>
using is_detected = detail::is_detected_impl<void, Op, Args...>;

std::is_detectedはまだないので自作すればいい

SFINAE3
template<class T>
using HasFuncOp = decltype(std::declval<T>().func());

template<class T>
using HasFunc = is_detected<HasFuncOp, T>;

これまでの書き方と比べてだいぶすっきりしたと思う
もっと広まってほしい

参考

https://ja.wikipedia.org/wiki/SFINAE
http://en.cppreference.com/w/cpp/experimental/is_detected