Я использую 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?
Измените эту строку:
struct any { template <typename T> operator T() { } };
чтобы:
struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};
У нас есть оператор неявного приведения как lvalue, так и rvalue. Итак, мы … хорошо?
Следующие работы (для маленьких 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
: /
Построение ответа от @ 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&&();
};
Похоже, что это работает во всех тех же случаях, что и предыдущий ответ, без экспоненциального масштабирования.