SFINAE用于具有默认参数的函数对象

时间:2018-06-11 12:34:18

标签: c++ lambda template-meta-programming sfinae

在编译时告诉lambda(或函数对象)是否有默认参数的最简单方法是什么? 例如:

auto f = [](int i=0){};
auto g = [](int i){};
static_assert(has_default_arg<decltype(f)>::value==true);
static_assert(has_default_arg<decltype(g)>::value==false);

2 个答案:

答案 0 :(得分:5)

我不相信没有某种功能静态反射就可以检测到这种情况。但是,您可以检查lambda是否可以使用零参数和一个参数进行调用。使用detection idiom的示例:

template <class T>
using invocable_zero = decltype(std::declval<T&>()());

template <class T, class X>
using invocable_one = decltype(std::declval<T&>()(std::declval<X>()));

template <class T, class X>
struct has_default_arg : std::conjunction< 
    std::experimental::is_detected<invocable_zero, T>,
    std::experimental::is_detected<invocable_one, T, X>
> { };

live example on wandbox.org

答案 1 :(得分:1)

NathanOliver中指出comment,您无法对普通函数对象执行此操作。所以我们只专注于lambda。

首先,我们可以创建一个帮助器类来检查是否可以使用从F通过索引序列Args...选择的参数调用Index_sequence

template <typename F, typename Index_sequence, typename... Args>
struct is_invocable_for_indices : std::false_type {};

template <typename F, size_t... Is, typename... Args>
struct is_invocable_for_indices<F, std::index_sequence<Is...>, Args...> 
    : std::is_invocable<F, std::tuple_element_t<Is, std::tuple<Args...>>...> {};

template <typename F, typename Index_sequence, typename... Args>
inline constexpr bool is_invocable_for_indices_v = is_invocable_for_indices<F, Index_sequence, Args...>::value;

// example use
auto f = [](int i = 0) {};
auto g = [](int i) {};
static_assert(is_invocable_for_indices_v<decltype(f), std::index_sequence<>, int>);
static_assert(!is_invocable_for_indices_v<decltype(g), std::index_sequence<>, int>);
static_assert(is_invocable_for_indices_v<decltype(g), std::index_sequence<0>, int>);

Args成为F的参数类型,可以通过decltype(&F::operator())检测到(这个想法来自this answer)。现在,您可以通过检查是否可以使用F的{​​{1}}个参数调用F来检查sizeof...(Args) - 1是否具有默认参数。因此,我们可以按如下方式定义Args

has_defulat_arg

LIVE EXAMPLE