如何确定std :: function的参数个数?

时间:2013-04-04 08:38:59

标签: c++ c++11 lambda template-meta-programming generic-programming

我有以下问题。假设您要编写可以采用lambda表达式的泛型函数。我知道如果参数是std :: function类型,那么我不仅可以使用lambdas,还可以使用函数,甚至指向函数的指针。所以在第一步,我做了以下几点:

void print(std::function<void(int, int)> fn) {
  fn(1,2);
}

int main() {

  print([](int i, int j) { std::cout << j <<','<<i<<'\n'; });
  return 0;
}

现在的问题是我想让这个函数变得通用,这意味着我不希望lambda表达式只有两个参数。 因此,我尝试将打印功能的签名更改为更通用的内容,如:

template <class function_type>
void print(function_type fn);

但现在的问题是该函数需要任何对象,我对此不满意。 但主要的问题是,我不知道对象fn可以接受多少参数。

所以在某种程度上我正在寻找一种编译方式来确定fn有多少参数,如果可能的话,将fn的类型改为std :: function。然后,鉴于我知道fn接受的参数数量,是否有一种通用的方法来包含要传递给fn的任意数量的参数?我甚至不知道这是否可以在C ++ 11中实现。我的意思是,鉴于参数的数量,有没有办法打包参数传递给fn?所以,如果有两个参数,那么我会调用

fn(arg1, arg2);

如果有三个:

fn(arg1, arg2, arg3);

等等。

谢谢大家的见解。

AA

3 个答案:

答案 0 :(得分:2)

以下代码段可能很有用。

这给出了std::function占用的参数数量

template <typename Signature>
struct count_args;

template <typename Ret, typename... Args>
struct count_args<std::function<Ret(Args...)>> {
    static constexpr size_t value = sizeof...(Args);
};

例如,以下代码编译(clang 3.2,gcc 4.7.2和icc 13.1.0)

static_assert(count_args<std::function<void()        >>::value == 0, "Ops!");
static_assert(count_args<std::function<void(int)     >>::value == 1, "Ops!");
static_assert(count_args<std::function<void(int, int)>>::value == 2, "Ops!");

据我了解,你想调用函数对象传递正确数量的参数,对吧?然后,对于每个参数,我们需要提供一个可转换为其类型的值。具有这种普遍性的解决方案非常困难(甚至不可能)。因此,我将提出两种选择。

1 每个参数都是其类型的值初始化对象。 (这是ecatmur suggested。)

template <typename Ret, typename... Args>
Ret call(const std::function<Ret(Args...)>& f) {
    return f(Args{}...); // for the intel compiler replace {} with ()
}

2 给出一个固定值,并从该值隐式初始化所有参数:

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<sizeof...(Args) == sizeof...(Vals), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val&, const Vals&... vals) {
    return f(vals...);
}

template <typename Ret, typename... Args, typename Val, typename... Vals>
typename std::enable_if<(sizeof...(Args) > sizeof...(Vals)), Ret>::type
call(const std::function<Ret(Args...)>& f, const Val& val, const Vals&... vals) {
    return call(f, val, val, vals...);
}

这三个重载是明确的,可以用作以下示例显示:

{
    std::function<char()> f = []() -> char {
        std::cout << "f() ";
        return 'A';
    };
    std::cout << call(f)    << std::endl; // calls f()
    std::cout << call(f, 0) << std::endl; // calls f()
}
{
    std::function<char(int)> f = [](int i) -> char {
        std::cout << "f(" << i << ") ";
        return 'B';
    };
    std::cout << call(f)    << std::endl; // calls f(0)
    std::cout << call(f, 1) << std::endl; // calls f(1)
}
{
    std::function<char(int, int)> f = [](int i, int j) -> char {
        std::cout << "f(" << i << "," << j << ") ";
        return 'C';
    };
    std::cout << call(f)    << std::endl; // calls f(0, 0)
    std::cout << call(f, 2) << std::endl; // calls f(2, 2)
}

答案 1 :(得分:0)

是的,您可以使用可变参数模板将任意数量的参数打包到fn

template <class function_type, class... Args>
void print(function_type fn, Args... args)
{
    //Call fn with args
    fn(std::forward<Args>(args...));
}

要了解参数包中有多少个参数,您可以使用sizeof...(args)

答案 2 :(得分:0)

要确定可调用的签名,您可以使用Inferring the call signature of a lambda or arbitrary callable for "make_function"中的解决方案。然后,您可以将可调用数据包打包到std::function,或创建标记并使用参数推断:

template<typename T> struct tag {};

template<typename F, typename... Args>
void print_impl(F &&fn, tag<void(Args...)>) {
  fn(Args{}...);
}

template<typename F>
void print(F &&fn) {
  print_impl(std::forward<F>(fn), tag<get_signature<F>>{});
}

注意这使用了值初始化的参数;如果你想要更复杂的东西,你可以建立一个std::tuple<Args...>并传递它,按照"unpacking" a tuple to call a matching function pointer调用它。