使用 C++ 检测惯用语计算任意可调用对象的参数
Counting arguments of an arbitrary callable with the C++ detection idiom
我一直在使用 C++ detection idiom create a metafunction for determining the number of arguments to an arbitrary callable. So far, I have this (full, compilable code at 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> { };
当 none 个可调用参数是左值引用时,这非常有用:
void foo(int i, int j) { }
static_assert(count_args<decltype(foo)>::value == 2, "");
但是当任何参数是左值引用时,这会失败(原因很明显,因为可调用原型有替换失败):
void bar(char i, bool j, double& k);
static_assert(count_args<decltype(bar)>::value == 3, "doesn't work");
有谁知道如何概括这个想法以使其也适用于左值引用?
以下作品(小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&&();
};
在没有指数缩放的情况下,这似乎适用于与上一个答案相同的所有情况。
更改此行:
struct any { template <typename T> operator T() { } };
至:
struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};
我们有左值和右值隐式转换运算符。那么,我们...好吗?
我一直在使用 C++ detection idiom create a metafunction for determining the number of arguments to an arbitrary callable. So far, I have this (full, compilable code at 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> { };
当 none 个可调用参数是左值引用时,这非常有用:
void foo(int i, int j) { }
static_assert(count_args<decltype(foo)>::value == 2, "");
但是当任何参数是左值引用时,这会失败(原因很明显,因为可调用原型有替换失败):
void bar(char i, bool j, double& k);
static_assert(count_args<decltype(bar)>::value == 3, "doesn't work");
有谁知道如何概括这个想法以使其也适用于左值引用?
以下作品(小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&&();
};
在没有指数缩放的情况下,这似乎适用于与上一个答案相同的所有情况。
更改此行:
struct any { template <typename T> operator T() { } };
至:
struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};
我们有左值和右值隐式转换运算符。那么,我们...好吗?