I'm learning variadic templates in C++11. How can I call test.finder
as a functional argument of 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;
}
My expected result is 0.1 * (2 + 0) = 0.2.
Your solution has a lot of problems:
1) You can not convert a member function to a std::function
, because the member function can never be called without an object. You can solve this problem by making the member function static
or use std::bind
or use a lambda.
2) You can not have a variadic argument list in the mid of method parameters. Variadic arguments must go to the end of the parameter list.
3) You can not deduce template arguments "on the fly" inside the parameter list itself. Solution: Use a new template parameter with a default template argument.
4) I see no chance to have default arguments for a function which we want to use in a std::function with reulting different signature double(double)
vs 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;
}
You can simply do this:
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;
}
and the result is:
0.2
0.5
I guess you're a bit mixing the variadic template part with a bit of design-flaw
Let's start.
Preamble : the correct way to deal with variadic templates is to use rvalue-reference
and std::forward
to achieve perfect forwarding .
1) The easy way : you don't need class at all
you're actually not referring to any member so a class bring only complexity. It's better to refer to a free function for these cases
#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;
}
your function accept 2 parameters so you have to pass zero as second argument.
2) Using a class
The problem with your attempt is you want to call c_test.var_finder
with a function of c_test
itself. Now you have to figure what kind of design you want. You can make 2 assumption. First is "I want anyway a finder function inside my class", then you have to make it static
because it really does not use class member so you don't need an instance of c_test
, right? so using a free function or a static member function leaves the var_finder
implementation and you just have to call it this way
#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;
}
second assumption you can do is "nope, I want any function member to be called with var_finder regardless where it comes from". I strongly discourage this approach because is carving a solution from a bad design, so I suggest to rethink your design to fall to solution 1 or 2.
3) Bonus : a nice design
You can add a non-variadic function and delegate the usage to the use of a lambda, which allow you to use a member function inside it without defining a variadic template to deal with that (and it is the common implementation for the std library functions).
#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;
}
There are multiple things to deal with here.
First, put the parameter pack args
at the end, otherwise the compiler doesn't know where to stop matching arguments with it (unless you specify the template arguments).
template<typename... Args>
double var_finder(double c, std::function<double (Args... args)> func, Args... args)
Second, don't name the parameter in std::function
; also just don't use std::function
, use a function pointer instead.
template<typename... Args>
double var_finder(double c, double (*func)(Args...), Args... args)
Third, make finder
static, otherwise you run into member function pointers and the syntax is more difficult to grasp (more on that later).
static double finder(double a, double b = 0)
Third, provide the optional argument for finder
. Otherwise, the parameter pack can't be deduced because it doesn't match both func
and args
.
std::cout << test.var_finder(0.1, c_test::finder, 2.0, 0.0) << std::endl;
This gives you the following.
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;
}
If you really need finder
to not be static, then you have to deal with member function pointer syntax.
First, the parameter func
must be a member function pointer.
double (c_test::*func)(Args...) // parameter declaration
&c_test::finder // argument in call
Then, you need to pass a c_test
variable to var_finder
in order to call 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;
That gives you the following code.
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;
}
If you want to be able to call finder
without specifying the optional parameter, then you need some intermediary, like a lambda.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.