c ++ 11:使用向量的元素调用可变参数函数,并自动推导参数数量

c++11: Calling a variadic function with the elements of a vector, and deduce number of arguments automatically

尝试扩展此处找到的示例:

基本上,我想从自动传入的函数中推断出参数的数量(这显然不适用于重载函数)。我希望它也能与仿函数和 lambda 一起使用。

无法编译对 call_helper 的调用:错误:模板参数解析错误

我似乎无法弄清楚如何将 returns 参数数量作为模板参数传递给 constexpr。

这是我目前拥有的:

#include <vector>

template <typename R, typename ... T>
constexpr std::size_t get_args_count( R(*f)(T ...))
{
   return sizeof...(T);
}

template< std::size_t... Ns >
struct indices {
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices {
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 > {
    typedef indices<> type;
};

void abc(int) {}
void abc2(int, int) {}

// helper function because we need a way
// to deduce indices pack

template<typename Func, size_t... Is>
void call_helper2(Func f, const std::vector<int>& args, indices<Is...>)
{
    f( args[Is]... ); // expand the indices pack
}

template<typename Func, size_t N>
void call_helper(Func f, const std::vector<int>& args)
{
    call_helper2(f, args, typename make_indices<N>::type());
}

template<typename Func>
void call(Func f, const std::vector<int>& args)
{
    if (args.size() < get_args_count(f)) throw 42;
    call_helper<get_args_count(decltype(f))>(f, args); // error: parse error in template argument list
}

int main()
{
    struct F
    {
        void operator()(int, int, int, int) {}
    };

    std::vector<int> v(4);
    call(&abc2, v);
    call(&abc, v);
    call([&](int, int, int) { (void)v.empty(); }, v);
    call(F(), v);
}

我错过了什么或做错了什么?感谢任何帮助。

编辑:添加了仿函数和 lambda 用例

有两个错误:

  1. call_helper<get_args_count(decltype(f))>(f, args) 中,get_args_count(decltype(f)) 没有意义,因为 get_args_count 采用函数指针而不是类型(显然);
  2. 函数参数永远不能是 constexpr(我会让你搜索为什么)。

如何修复?

您需要尽快提取 f 的参数数量,以免它变成在 constexpr 上下文中可用的表达式以外的东西。

#include <vector>

template< std::size_t... Ns >
struct indices {
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices {
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 > {
    typedef indices<> type;
};

void abc(int) {}
void abc2(int, int) {}

// helper function because we need a way
// to deduce indices pack

template<typename Func, size_t... Is>
void call_helper2(Func f, const std::vector<int>& args, indices<Is...>)
{
    f( args[Is]... ); // expand the indices pack
}

template<typename Func, size_t N>
void call_helper(Func f, const std::vector<int>& args)
{
    call_helper2(f, args, typename make_indices<N>::type());
}

template<class R, class ... T>
void call(R(*f)(T ...), const std::vector<int>& args)
{
    if (args.size() < sizeof...(T)) throw 42;
    call_helper<
        decltype(f), sizeof...(T)
    >(f, args);
}

int main()
{
    std::vector<int> v(2);
    call(&abc2, v);
}

YSC 的答案解决了常规函数的问题,但不适用于 lambda 和仿函数。为了使其也适用于仿函数和 lambda,我必须添加 call 的重载(基于 中的解决方案):

template< std::size_t... Ns >
struct indices {
    typedef indices< Ns..., sizeof...( Ns ) > next;
};

template< std::size_t N >
struct make_indices {
    typedef typename make_indices< N - 1 >::type::next type;
};

template<>
struct make_indices< 0 > {
    typedef indices<> type;
};

void abc(int) {}
void abc2(int, int) {}

// helper function because we need a way
// to deduce indices pack

template<typename Func, size_t... Is>
void call_helper2(Func f, const std::vector<int>& args, indices<Is...>)
{
    f( args[Is]... ); // expand the indices pack
}

template<typename Func, size_t N>
void call_helper(Func f, const std::vector<int>& args)
{
    call_helper2(f, args, typename make_indices<N>::type());
}

template<class R, class ... T>
void call(R(*f)(T ...), const std::vector<int>& args)
{
    // this overload is used for regular functions
    if (args.size() < sizeof...(T)) throw 42;
    call_helper<decltype(f), sizeof...(T)>(f, args);
}

template <typename T>
struct func_traits:
    public func_traits<decltype(&T::operator())>
{};

template <typename C, typename R, typename... Args>
struct func_traits<R(C::*)(Args...) const>
{
    enum { arity = sizeof...(Args) };
};

template<typename Func>
void call(Func f, const std::vector<int>& args)
{
    // this overload is used for functors and lambdas
    using traits = func_traits<decltype(f)>;
    if (args.size() < traits::arity) throw 42;
    call_helper<decltype(f), traits::arity>(f, args);
}

int main()
{
    struct F
    {
        void operator()(int, int, int, int) const {}
    };

    std::vector<int> v(4);
    call(&abc2, v);
    call(&abc, v);
    call([&](int, int, int) { (void)v.empty(); }, v);
    call(F(), v);
}