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

我认为如果没有某种函数静态反射,就不可能检测到这一点。但是,您可以检查 lambda 是否同时具有零个和 1 个参数。使用检测习惯用语的示例:

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>
> { };

wandbox.org 上的现场示例

正如 NathanOliver 在评论中指出的那样,你不能对普通函数对象执行此操作。所以我们只关注lambda。

首先,我们可以创建一个帮助类,通过索引序列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())检测(这个想法来自这个答案(。现在,您可以通过检查是否可以使用Args的前sizeof...(Args) - 1个参数调用F来检查F是否具有默认参数。因此,我们可以定义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)>);

现场示例

最新更新