使用可变参数模板参数调用零参数模板函数指针?

时间:2014-12-10 13:46:03

标签: c++ c++11 variadic-templates template-function

以下是Functional C++博客帖子中的代码段,描述了如何实施广义功能评估。

我的问题是如何声明模板函数指针f,如R(C :: * f)()没有参数,仍然可以用Args调用它?

// functions, functors, lambdas, etc.
template<
    class F, class... Args,
    class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type,
    class = typename std::enable_if<!std::is_member_object_pointer<F>::value>::type
    >
auto eval(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...))
{
    return f(std::forward<Args>(args)...);
}

// const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)() const, const C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

template<class R, class C, class... Args>
auto eval(R(C::*f)() const, C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

// non-const member function
template<class R, class C, class... Args>
auto eval(R(C::*f)(), C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

// member object
template<class R, class C>
auto eval(R(C::*m), const C& c) -> const R&
{
    return c.*m;
}

template<class R, class C>
auto eval(R(C::*m), C& c) -> R&
{
    return c.*m;
}

struct Bloop
{
    int a = 10;
    int operator()(){return a;}
    int operator()(int n){return a+n;}
    int triple(){return a*3;}
};

int add_one(int n)
{
    return n+1;
}

int main()
{
    Bloop bloop;

    // free function
    std::cout << eval(add_one,0) << "\n";

    // lambda function
    std::cout << eval([](int n){return n+1;},1) << "\n";

    // functor
    std::cout << eval(bloop) << "\n";
    std::cout << eval(bloop,4) << "\n";

    // member function
    std::cout << eval(&Bloop::triple,bloop) << "\n";

    // member object
    eval(&Bloop::a,bloop)++; // increment a by reference
    std::cout << eval(&Bloop::a,bloop) << "\n";

    return 0;
}

例如,当我尝试:

struct Bloop
{
    int a = 10;
    int operator()(){return a;}
    int operator()(int n){return a+n;}
    int triple(){return a*3;}
    int foo(int n) {return n;}
};

template <typename R, typename C, typename... Args>
void eval (R(C::*func)(), C& c, Args... args) {
    (c.*func)(args...);
}

int main()
{
    Bloop bloop;

    eval(&Bloop::foo, bloop, 5);

    return 0;
}

我收到此错误:

main.cpp: In function 'int main()':
main.cpp:27:31: error: no matching function for call to 'eval(int (Bloop::*)(int), Bloop&, int)'
     eval(&Bloop::foo, bloop, 5);
                               ^
main.cpp:27:31: note: candidate is:
main.cpp:19:6: note: template<class R, class C, class ... Args> void eval(R (C::*)(), C&, Args ...)
 void eval (R(C::*func)(), C& c, Args... args) {
      ^
main.cpp:19:6: note:   template argument deduction/substitution failed:
main.cpp:27:31: note:   candidate expects 1 argument, 2 provided
     eval(&Bloop::foo, bloop, 5);
                               ^

如果我宣布func类似于R(C::*func)(int),则会编译。

2 个答案:

答案 0 :(得分:5)

博客文章中的代码不正确(或至少不完整);它只适用于无参数函数。你可以像这样更正确地写eval

template<class R, class C, class... T, class... Args>
auto eval(R(C::*f)(T...), C& c, Args&&... args) -> R
{
    return (c.*f)(std::forward<Args>(args)...);
}

请注意T...参数包,以获取指向成员函数类型的指针的参数。这是来自Args&&...的独特类型包,因为这两个包可以推断出不同。

答案 1 :(得分:1)

通过避免分析指向成员函数的指针和指向成员数据的指针,并简单地接受调用定义明确的任何内容,可以使此代码更简单,更通用:

#define RETURNS(...) \
  -> decltype(__VA_ARGS__) { \
    return (__VA_ARGS__); \
  }

// Function object type
template<class F, class... Args>
auto eval(F&& f, Args&&... args)
RETURNS(std::forward<F>(f)(std::forward<Args>(args)...))

// pointer to member function, object reference
template<class PMF, class C, class... Args>
auto eval(PMF&& pmf, C&& c, Args&&... args)
RETURNS((std::forward<C>(c).*std::forward<PMF>(pmf))(std::forward<Args>(args)...))

// pointer to member data, object reference
template<class PMD, class C>
auto eval(PMD&& pmd, C&& c)
RETURNS(std::forward<C>(c).*std::forward<PMD>(pmd))

当我们处于它的时候,除了对象引用之外,我们还可以支持带有对象指针的指针到成员的省略情况以获得完整性,特别是考虑到示例代码需要它们来评估eval(&Bloop::a,&bloop)++

// pointer to member data, object pointer
template<class PMD, class P>
auto eval(PMD&& pmd, P&& p)
RETURNS((*std::forward<P>(p)).*std::forward<PMD>(pmd))

// pointer to member function, object pointer
template<class PMF, class P, class... Args>
auto eval(PMF&& pmf, P&& p, Args&&... args)
RETURNS(((*std::forward<P>(p)).*std::forward<PMF>(pmf))(std::forward<Args>(args)...))

DEMO

(好吧,也许“更简单”是一个糟糕的选择。“更简洁”或“简洁”可能更准确。)