使用C++检测习惯用法计算任意可调用对象的参数

Counting arguments of an arbitrary callable with the C++ detection idiom

本文关键字:调用 对象 参数 任意可 计算 C++ 检测 习惯 惯用法 使用      更新时间:2023-10-16

我一直在使用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> { };

当所有可调用参数都不是左值引用时,这种方法非常有效:

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:/

更改此行:

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

至:

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

实例

我们有一个左值和右值隐式强制转换运算符。所以,我们。。。好的

在@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&&();
};

在没有指数缩放的情况下,这似乎在与前面的答案相同的所有情况下都有效。

演示