初めに
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