templates — Подсчет аргументов произвольного вызываемого объекта с идиомой обнаружения C ++

Я использую C ++ идиома обнаружения создать метафункцию для определения количества аргументов для произвольного вызываемого объекта. До сих пор у меня есть это (полный, скомпилированный код на http://ideone.com/BcgDhv):

static constexpr auto max_num_args = 127;

struct any { template <typename T> operator T() { } };

template <typename F, typename... Args>
using callable_archetype = decltype( declval<F>()(declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = is_detected<callable_archetype, F, Args...>;

template <typename F, size_t I = 0,  typename... Args>
struct count_args
: conditional<is_callable_with_args<F, Args...>::value,
integral_constant<size_t, I>,
count_args<F, I+1, Args..., any>
>::type::type
{ };

template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> : integral_constant<size_t, max_num_args> { };

Это прекрасно работает, когда ни один из вызываемых аргументов не является ссылками lvalue:

void foo(int i, int j) { }
static_assert(count_args<decltype(foo)>::value == 2, "");

Но когда какой-либо из аргументов является ссылкой на lvalue, это терпит неудачу (по очевидным причинам, поскольку вызываемый архетип имеет ошибку замещения):

void bar(char i, bool j, double& k);
static_assert(count_args<decltype(bar)>::value == 3, "doesn't work");

Кто-нибудь знает, как обобщить эту идею, чтобы она работала и со ссылками на lvalue?

4

Решение

Измените эту строку:

struct any { template <typename T> operator T() { } };

чтобы:

struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};

живой пример

У нас есть оператор неявного приведения как lvalue, так и rvalue. Итак, мы … хорошо?

1

Другие решения

Следующие работы (для маленьких max_num_args):

struct any    { template <typename T> operator T(); };
struct anyref { template <typename T> operator T&(); };

template <typename F, typename... Args>
using callable_archetype = decltype(std::declval<F>()(std::declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = std::is_detected<callable_archetype, F, Args...>;

template <typename F, size_t I = 0,  typename... Args>
struct count_args
: std::conditional<is_callable_with_args<F, Args...>::value,
std::integral_constant<std::size_t, I>,
std::integral_constant<std::size_t,
std::min(count_args<F, I+1, Args..., any>::value,
count_args<F, I+1, Args..., anyref>::value)>
>::type::type
{};

template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> :
std::integral_constant<std::size_t, max_num_args> {};

демонстрация

Но код должен быть оптимизирован, так как сложность 2**max_num_args : /

3

Построение ответа от @ Jarod42, немного лучшее определение any кажется, добивается цели в подавляющем большинстве случаев (исключая случаи, которые вызывают callable_archetype быть ошибкой замещения по другим причинам; например, классы с конструкторами удаленных копий, вызов которых в любом случае не будет действительным):

struct any {
template <typename T,
typename = enable_if_t<
not is_same<T, remove_reference_t<T>>::value
>
>
operator T();

template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&();

template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&&();
};

Похоже, что это работает во всех тех же случаях, что и предыдущий ответ, без экспоненциального масштабирования.

демонстрация

0
По вопросам рекламы [email protected]