SFINAE для объекта функции с аргументом по умолчанию - PullRequest
0 голосов
/ 11 июня 2018

Какой самый простой способ сказать во время компиляции, имеет ли лямбда (или объект функции) аргумент по умолчанию или нет?Пример:

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 голосов
/ 11 июня 2018

Как указывает NathanOliver в комментарии , вы не можете сделать это для обычного функционального объекта.Поэтому мы сосредоточимся только на лямбде.

Во-первых, мы можем создать вспомогательный класс, который проверяет, можно ли вызывать 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()) (идея приходит из этого ответа ).Теперь вы можете проверить, имеет ли F аргумент по умолчанию, проверив, можно ли вызвать F с первыми sizeof...(Args) - 1 аргументами Args.Таким образом, мы можем определить 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

0 голосов
/ 11 июня 2018

Я не верю, что это можно обнаружить без какой-либо функции статического отражения.Однако вы можете проверить, является ли лямбда доступной как с нулевым, так и с одним аргументом.Пример использования идиомы обнаружения :

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

...