调用类内的函数的C ++ 11可变参数模板

时间:2020-06-05 08:23:30

标签: c++ c++11 variadic-templates std-function callable

我正在学习C ++ 11中的可变参数模板。如何将test.finder称为test.var_finder的功能参数?

#include <functional>
#include <iostream>

class c_test {
    public:

        double finder(double a, double b = 0) {
            return a + b;
        };


        template<typename... Args>
        double var_finder(double c, Args... args, std::function<double (Args... args)> func) {
            return c * func(args...);
        };
};

int main () {
    c_test test;

    std::cout << test.var_finder(0.1, 2, test.finder) << std::endl;

    return 0;
}

我的预期结果是0.1 *(2 + 0)= 0.2。

4 个答案:

答案 0 :(得分:1)

您的解决方案有很多问题:

1)您不能将成员函数转换为std::function,因为没有对象就无法调用成员函数。您可以通过使成员函数static或使用std::bind或使用lambda来解决此问题。

2)方法参数中间不能有可变参数列表。可变参数必须转到参数列表的末尾。

3)您不能在参数列表本身内部“即时”推断模板参数。解决方案:使用带有默认模板参数的新模板参数。

4)我看不出要在std :: function中使用的函数具有默认参数的可能性,该函数会得出不同的签名double(double)double(double,double)

class c_test {
    public:

        static double finders(double a, double b = 0) {
            return a + b;
        };

        double finder(double a, double b = 0) {
            return a + b;
        };


        template<typename... Args, typename F= std::function<double(Args...)> >
            double var_finder(double c, F func, Args... args) {
                return c * func(args...);
            }
};

int main () {
    c_test test;

    // use static member
    std::cout << test.var_finder(0.1, test.finders,  2.,3.) << std::endl;

    // use lambda instead of all the hacky things
    std::cout << test.var_finder(0.1, [&test](double p1,double p2){ return test.finder(p1,p2); },  2.,3.) << std::endl;

    return 0;
}

答案 1 :(得分:1)

您可以简单地执行以下操作:

class c_test 
{
public:

   double finder(double a, double b = 0) 
   {
      return a + b;
   };


   template<typename Func, typename... Args>
   double var_finder(double c, Func func, Args... args) 
   {
      return c * func(args...);
   };
};

int main() 
{
   c_test test;
   auto f_1 = std::bind(std::mem_fn(&c_test::finder), test, std::placeholders::_1, 0);
   auto f_2 = std::bind(std::mem_fn(&c_test::finder), test, std::placeholders::_1, std::placeholders::_2);

   std::cout << test.var_finder(0.1, f_1, 2.0) << std::endl;
   std::cout << test.var_finder(0.1, f_2, 2.0, 3.0) << std::endl;

   return 0;
}

结果是:

0.2
0.5

答案 2 :(得分:1)

我想您是将可变参数模板部分与一些设计缺陷混为一谈

开始吧。

序言:处理可变参数模板的正确方法是使用rvalue-referencestd::forward实现 perfect forwarding 。< / p>

1) 简单方法:您根本不需要上课

您实际上并不是在指任何成员,因此类只会带来复杂性。在这种情况下,最好引用自由函数

#include <functional>
#include <iostream>

double finder(double a, double b = 0) {
    return a + b;
};

template<typename Func, typename... Args>
double var_finder(double c, Func&& f, Args&&... args) {
    return c * std::forward<Func>(f)(std::forward<Args>(args)...);
};

int main () {
    std::cout << var_finder(0.1, finder, 2, 0) << std::endl;

    return 0;
}

Demo 1

您的函数接受2个参数,因此您必须将零作为第二个参数传递。

2)使用课程

尝试的问题是您想使用c_test.var_finder本身的功能来调用c_test。现在,您必须确定想要哪种设计。您可以做出2个假设。首先是“无论如何我都想在我的类中使用查找器功能”,然后必须将其设为static,因为它实际上并不使用类成员,因此您不需要c_test的实例,对吗?因此使用自由函数或静态成员函数将保留var_finder实现,而您只需要以这种方式调用

#include <functional>
#include <iostream>

class c_test {
    public:
        static double finder(double a, double b = 0) {
            return a + b;
        };


        template<typename Func, typename... Args>
        double var_finder(double c, Func&& f, Args&&... args) {
            return c * std::forward<Func>(f)(std::forward<Args>(args)...);
        };
};

int main () {
    c_test test;

    std::cout << test.var_finder(0.1, &c_test::finder, 2, 0) << std::endl;

    return 0;
}

Demo 2

您可以做的第二个假设是“不,我希望使用var_finder调用任何函数成员,无论它来自何处”。我强烈不建议采用这种方法,因为这是从不良设计中找出解决方案的,因此建议您重新考虑您的设计,使其属于解决方案1或2。

3)奖励:一个不错的设计

您可以添加一个非可变函数并将用法委派给lambda的使用,这使您可以在其中使用成员函数而无需定义可变参数模板来处理该函数(这是该函数的常见实现std库函数)。

#include <functional>
#include <iostream>

double finder(double a, double b = 0) {
    return a + b;
};

template<typename Func, typename... Args>
double var_finder(double c, Func&& f, Args&&... args) {
    return c * std::forward<Func>(f)(std::forward<Args>(args)...);
};

template<typename Func, typename... Args>
double var_finder(double c, Func&& f) {
    return c * std::forward<Func>(f)();
};

class c_test
{
public:
    double finder(double a, double b = 0) {
        return a + b;
    };
};

int main () {
    double a = 2.0;
    double b = 0.0;

    // use as usual
    std::cout << var_finder(0.1, finder, a, b) << std::endl;

    // use with lambda
    std::cout << var_finder(0.1, [a,b](){ return a+b; }) << std::endl;

    // lambda with member argument, less gruesome than a variadic template calling a member function
    c_test c;
    std::cout << var_finder(0.1, [a,b, &c](){ return c.finder(a,b); }) << std::endl;

    return 0;
}

Bonus Demo

答案 3 :(得分:0)

这里有很多事情要处理。

首先,将参数包args放在末尾,否则编译器将不知道从何处停止匹配参数(除非您指定模板参数)。

template<typename... Args>
double var_finder(double c, std::function<double (Args... args)> func, Args... args)

第二,不要在std::function中命名参数;也只是不使用std::function,而是使用函数指针。

template<typename... Args>
double var_finder(double c, double (*func)(Args...), Args... args)

第三,将finder设为静态,否则会遇到成员函数指针,并且语法更难以掌握(稍后会详细介绍)。

static double finder(double a, double b = 0)

第三,为finder提供可选参数。否则,无法推导出参数包,因为它与funcargs都不匹配。

std::cout << test.var_finder(0.1, c_test::finder, 2.0, 0.0) << std::endl;

这将为您提供以下内容。

class c_test {
public:
    static double finder(double a, double b = 0) {
        return a + b;
    }

    template<typename... Args>
    double var_finder(double c, double (*func)(Args...), Args... args) {
        return c * func(args...);
    }
};

int main () {
    c_test test;

    std::cout << test.var_finder(0.1, c_test::finder, 2.0, 0.0) << std::endl;
}

Demo

如果您确实需要finder不是静态的,则必须处理成员函数指针语法。

首先,参数func必须是成员函数指针。

double (c_test::*func)(Args...) // parameter declaration
&c_test::finder // argument in call

然后,您需要将c_test变量传递给var_finder才能调用func

template<typename... Args>
double var_finder(double c, double (c_test::*func)(Args...), c_test test, Args... args) {
    return c * (test.*func)(args...);
}
// [...]
std::cout << test.var_finder(0.1, &c_test::finder, test, 2.0, 0.0) << std::endl;

这将为您提供以下代码。

class c_test {
public:
    double finder(double a, double b = 0) {
        return a + b;
    }

    template<typename... Args>
    double var_finder(double c, double (c_test::*func)(Args...), c_test test, Args... args) {
        return c * (test.*func)(args...);
    }
};

int main () {
    c_test test;

    std::cout << test.var_finder(0.1, &c_test::finder, test, 2.0, 0.0) << std::endl;
}

Demo

如果您希望能够在不指定可选参数的情况下调用finder,则需要一些中介,例如lambda。