带有默认参数的函数对象的 SFINAE

SFINAE for function object with default argument

在编译时判断 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);

我不相信如果没有某种函数静态反射就不可能检测到这一点。但是,您可以检查 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

作为NathanOliver pointed out in the ,您不能对普通函数对象执行此操作。所以我们只关注lambda。

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

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>);

ArgsF的参数类型,可以通过decltype(&F::operator())检测(思路来自this answer)。现在您可以检查 F 是否有默认参数,方法是检查 F 是否可以用 Args 的前 sizeof...(Args) - 1 个参数调用。所以我们可以这样定义has_defulat_arg

template <typename F, typename OperatorType>
struct has_defulat_arg_impl : std::false_type {};

template <typename F, typename R, typename... Args>
struct has_defulat_arg_impl<F, R(F::*)(Args...) const>    
    : is_invocable_for_indices<F, std::make_index_sequence<sizeof...(Args) - 1>, Args...> {};

// specialization for the case where sizeof...(Args) == 0
template <typename F, typename R>
struct has_defulat_arg_impl<F, R(F::*)() const> : std::false_type {};

template <typename F>
using has_defulat_arg = has_defulat_arg_impl<F, decltype(&F::operator())>;

template <typename F>
inline constexpr bool has_defulat_arg_v = has_defulat_arg<F>::value;

// example use
auto f = [](int i = 0) {};
auto g = [](int i) {};
static_assert(has_defulat_arg_v<decltype(f)>);
static_assert(!has_defulat_arg_v<decltype(g)>);

LIVE EXAMPLE