c ++将具有可变数量参数的函数作为参数传递给其他函数

时间:2016-12-17 14:23:00

标签: c++ variadic-functions

我写了一个C ++类来解析像“2 * SQRT(5)+ 1”这样的表达式。我创建了一个名为c_function的类,它“代表”常用的数学函数,如sqrtsincos等。如下所示:

class c_function {
  std::string name;
  double (*function)(double);

public:
  c_function(std::string fname, double (*ffunction)(double)) {
    name = fname;
    function = ffunction;
  }

// ...
};

然后我有一个不同的类,其中包含std::vectorc_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类添加自定义函数,并且还希望支持具有多个参数的自定义函数(例如,定义返回的AREA(a, b)函数两个值a和b)的乘积。

我通过向argumentCount类添加参数计数器c_function和更多函数“属性”来实现这一点:

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类:

// 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班级
myExpression.addFunction("AREA", Expression_Area);

这很好用,这样我还可以添加更多的函数“属性”和函数构造函数,允许任意数量的参数,但是

  1. 支持的参数数量始终存在限制
  2. 代码变得丑陋,因为参数的数量可能不同,有多个构造函数,添加函数的方法和表达式解释中的代码。
  3. 我想知道是否有一种方法可以支持具有更多一般参数的函数。我尝试将c_function类更改为:

    class c_function {
      std::string name;
      unsigned int argumentCount;
      double (*function)(...);
      // ...
    
    };
    

    但这不起作用,因为(...)不接受具有固定数量参数的函数。

    有没有办法用一个构造函数,一个函数“property”等来解决这个问题?

1 个答案:

答案 0 :(得分:3)

C ++ 11以后

在C ++ 11中,您可以使用可变参数模板来声明一个类,该类将使用具有可变数量参数的函数:

#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;
}

您可以调整此基本解决方案,了解您需要如何使用课程。

C ++ 98

Pre C ++ 11,你可能会被迫创建带有不同参数数量的函数的类,所以你的类看起来像这样:

#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类修改为:

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_;
};