c ++将具有可变数量参数的函数作为参数传递给其他函数
c++ passing functions with variable number of arguments as argument to other function
我写了一个 C++ class 来解析像“2 * SQRT(5) + 1”这样的表达式。我创建了一个名为 c_function
的 class,它 "represents" 通常的数学函数,如 sqrt
、sin
、cos
等。如下所示:
class c_function {
std::string name;
double (*function)(double);
public:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
function = ffunction;
}
// ...
};
然后我有一个不同的 class,其中包含 std::vector
这些 c_function
对象:
class expression {
std::vector<c_function> functions;
// etc...
public:
// ctor:
expression(/* ... */) {
// ...
functions.push_back(c_function("SQRT", sqrt));
functions.push_back(c_function("SIN" , sin));
functions.push_back(c_function("COS" , cos));
// ...
}
};
重点是所有这些函数都有一个参数。这在大多数情况下都很好,但我想启用向 expression
class 添加自定义函数,并且还希望支持具有多个参数的自定义函数(例如,定义一个函数 AREA(a, b)
即 returns 两个值 a 和 b 的乘积)。
我通过向 c_function
class:
添加参数计数器 argumentCount
和更多函数 "properties" 来做到这一点
class c_function {
std::string name;
unsigned int argumentCount;
double (*function1)(double);
double (*function2)(double, double);
// ...
};
并使用了两个构造函数:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
argumentCount = 1;
function1 = ffunction;
function2 = NULL;
};
c_function(std::string fname, double (*ffunction)(double, double)) {
name = fname;
argumentCount = 2;
function1 = NULL;
function2 = ffunction;
};
并将方法添加到 expression
class:
// add custom function with one argument
void addFunction(std::string fname, double (*ffunction)(double));
// add custom function with two arguments
void addFunction(std::string fname, double (*ffunction)(double, double));
这样就可以定义
double Expression_Area(double width, double height) {
return (width * height);
}
并将其介绍给 expression
class 和
myExpression.addFunction("AREA", Expression_Area);
这很好用,这样我还可以添加更多函数 "properties" 和允许任意数量参数的函数构造函数,但是
- 支持的参数数量总是有限制的
- 由于参数的数量可能不同,代码会因具有多个构造函数、添加函数的方法以及表达式解释内的代码而变得难看。
我想知道是否有更通用的方法来支持具有任意数量参数的函数。我尝试将 c_function
class 更改为:
class c_function {
std::string name;
unsigned int argumentCount;
double (*function)(...);
// ...
};
但这不起作用,因为 (...)
不接受具有固定数量参数的函数。
有没有办法用一个构造函数、一个函数 "property" 等来解决这个问题?
C++11 以上
在 C++11 中,您可以使用可变参数模板来声明一个 class,它将接受一个参数数量可变的函数:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
template <typename... Args>
class Foo {
public:
Foo (const std::string& name, double (*func)(Args...))
: name_{name}, func_{func} {}
double call(Args... args) {
return func_(args...);
}
// Thanks to W.F. for reminding me of the operator() overload
// This does the same thing as a call to the "call" method.
double operator()(Args... args) {
return func_(args...);
}
private:
std::string name_;
double (*func_)(Args...);
};
int main() {
Foo<double> t1("test1", bar);
Foo<double, double> t2("test2", bar2);
// NOTE: in C++17 you can declare Foo without the template
// arguments: they will be deduced.
// Foo t1("test1", bar); // C++17
// Foo t2("test2", bar2); // C++17
std::cout << t1.call(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
您可以根据 classes 的工作方式调整此基本解决方案。
C++98
在 C++11 之前,您可能会被迫创建 classes,它们接受具有不同数量参数的函数,因此您的 classes 看起来像这样:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
class Foo1 {
public:
// let's typedef the function pointer for two reasons:
// 1. readability
// 2. duplicating the class for a different number of arguments
// means we need to do less modifications to the code because
// we can catch a few changes in one line here.
typedef double (*Function)(double);
Foo1 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x) {
return func_(x);
}
private:
std::string name_;
Function func_;
};
class Foo2 {
public:
typedef double (*Function)(double, double);
Foo2 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x, double y) {
return func_(x, y);
}
private:
std::string name_;
Function func_;
};
// etc. for classes Foo3, Foo4, ... up until you think you will
// need no more.
int main() {
Foo1 t1("test1", bar);
Foo2 t2("test2", bar2);
std::cout << t1(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
这里有一点代码重复,但还算不错。
备注
最后,(虽然我没有在上面展示,因为我认为这是不言而喻的)只要有代码重复,就模板化类型,这样你就可以尽可能地减少它。因此,例如,您可能会考虑将上面的 C++11 Foo
class 修改为:
template <typename T, typename... Args>
class Foo {
public:
typedef T (*Function)(Args...);
Foo (const std::string& name, const Function func)
: name_{name}, func_{func} {}
T operator()(Args... args) {
return func_(args);
}
private:
std::string name_;
Function func_;
};
我写了一个 C++ class 来解析像“2 * SQRT(5) + 1”这样的表达式。我创建了一个名为 c_function
的 class,它 "represents" 通常的数学函数,如 sqrt
、sin
、cos
等。如下所示:
class c_function {
std::string name;
double (*function)(double);
public:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
function = ffunction;
}
// ...
};
然后我有一个不同的 class,其中包含 std::vector
这些 c_function
对象:
class expression {
std::vector<c_function> functions;
// etc...
public:
// ctor:
expression(/* ... */) {
// ...
functions.push_back(c_function("SQRT", sqrt));
functions.push_back(c_function("SIN" , sin));
functions.push_back(c_function("COS" , cos));
// ...
}
};
重点是所有这些函数都有一个参数。这在大多数情况下都很好,但我想启用向 expression
class 添加自定义函数,并且还希望支持具有多个参数的自定义函数(例如,定义一个函数 AREA(a, b)
即 returns 两个值 a 和 b 的乘积)。
我通过向 c_function
class:
argumentCount
和更多函数 "properties" 来做到这一点
class c_function {
std::string name;
unsigned int argumentCount;
double (*function1)(double);
double (*function2)(double, double);
// ...
};
并使用了两个构造函数:
c_function(std::string fname, double (*ffunction)(double)) {
name = fname;
argumentCount = 1;
function1 = ffunction;
function2 = NULL;
};
c_function(std::string fname, double (*ffunction)(double, double)) {
name = fname;
argumentCount = 2;
function1 = NULL;
function2 = ffunction;
};
并将方法添加到 expression
class:
// add custom function with one argument
void addFunction(std::string fname, double (*ffunction)(double));
// add custom function with two arguments
void addFunction(std::string fname, double (*ffunction)(double, double));
这样就可以定义
double Expression_Area(double width, double height) {
return (width * height);
}
并将其介绍给 expression
class 和
myExpression.addFunction("AREA", Expression_Area);
这很好用,这样我还可以添加更多函数 "properties" 和允许任意数量参数的函数构造函数,但是
- 支持的参数数量总是有限制的
- 由于参数的数量可能不同,代码会因具有多个构造函数、添加函数的方法以及表达式解释内的代码而变得难看。
我想知道是否有更通用的方法来支持具有任意数量参数的函数。我尝试将 c_function
class 更改为:
class c_function {
std::string name;
unsigned int argumentCount;
double (*function)(...);
// ...
};
但这不起作用,因为 (...)
不接受具有固定数量参数的函数。
有没有办法用一个构造函数、一个函数 "property" 等来解决这个问题?
C++11 以上
在 C++11 中,您可以使用可变参数模板来声明一个 class,它将接受一个参数数量可变的函数:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
template <typename... Args>
class Foo {
public:
Foo (const std::string& name, double (*func)(Args...))
: name_{name}, func_{func} {}
double call(Args... args) {
return func_(args...);
}
// Thanks to W.F. for reminding me of the operator() overload
// This does the same thing as a call to the "call" method.
double operator()(Args... args) {
return func_(args...);
}
private:
std::string name_;
double (*func_)(Args...);
};
int main() {
Foo<double> t1("test1", bar);
Foo<double, double> t2("test2", bar2);
// NOTE: in C++17 you can declare Foo without the template
// arguments: they will be deduced.
// Foo t1("test1", bar); // C++17
// Foo t2("test2", bar2); // C++17
std::cout << t1.call(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
您可以根据 classes 的工作方式调整此基本解决方案。
C++98
在 C++11 之前,您可能会被迫创建 classes,它们接受具有不同数量参数的函数,因此您的 classes 看起来像这样:
#include <iostream>
#include <string>
double bar(double x) {
return x;
}
double bar2(double x, double y) {
return x+y;
}
class Foo1 {
public:
// let's typedef the function pointer for two reasons:
// 1. readability
// 2. duplicating the class for a different number of arguments
// means we need to do less modifications to the code because
// we can catch a few changes in one line here.
typedef double (*Function)(double);
Foo1 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x) {
return func_(x);
}
private:
std::string name_;
Function func_;
};
class Foo2 {
public:
typedef double (*Function)(double, double);
Foo2 (const std::string& name, const Function func)
: name_(name), func_(func) {}
double operator()(double x, double y) {
return func_(x, y);
}
private:
std::string name_;
Function func_;
};
// etc. for classes Foo3, Foo4, ... up until you think you will
// need no more.
int main() {
Foo1 t1("test1", bar);
Foo2 t2("test2", bar2);
std::cout << t1(14) << ' ' << t2(14, 56) << std::endl;
return 0;
}
这里有一点代码重复,但还算不错。
备注
最后,(虽然我没有在上面展示,因为我认为这是不言而喻的)只要有代码重复,就模板化类型,这样你就可以尽可能地减少它。因此,例如,您可能会考虑将上面的 C++11 Foo
class 修改为:
template <typename T, typename... Args>
class Foo {
public:
typedef T (*Function)(Args...);
Foo (const std::string& name, const Function func)
: name_{name}, func_{func} {}
T operator()(Args... args) {
return func_(args);
}
private:
std::string name_;
Function func_;
};