Sometimes, we are supposed to accept uncertain number of arguments in C++.

In this post, I am going to introduce 3 ways to implement it, so that you can pick up one of them which is most fit for you in current situation.

1. va_list

va_list is coming from C lang. Had you ever considered how to implement the function "printf"? Of course, they are using va_list.

Here is a tiny example

/* output:
<stdin>:23 args_copy[0]=3
<stdin>:23 args_copy[1]=5
sum of: 1, 2, 3 = 9
*/
#include <stdarg.h>
#include <stdio.h>

int sum(int count, ...) {
  int sum = 0;
  va_list args;
  va_start(args, count);
  va_list args_copy;

  for (int j = 0; j < count; j++) {
    sum += va_arg(args, int);
    if (j == 0) {
      // args_copy will copy args to args_copy from current
      va_copy(args_copy, args);
    }
  }
  va_end(args);

  for (int j = 0; j < (count - 1); j++) {
    printf("%s:%d args_copy[%d]=%d\n", __FILE__, __LINE__, j,
           va_arg(args_copy, int));
  }
  va_end(args_copy);
  return sum;
}

int main() {
  printf("sum of: 1, 2, 3 = %d\n", sum(3, 1, 3, 5));
}

Please refer to this link and try yourself. You can also refer to this link for its documentation.

You will use va_start, va_copy, va_arg, va_end to initialize, copy, scan and finalize the args easily.

The positive is you can use it either in C or C++, but the negative is you are don't know the count and type of args.

2. initializer_list

initializer_list is a lightweight solution to pass a set of args to some function.

Here is a simple example for initializer_list

/* output
got: 1
got: 2
got: 3
got: 4
total size: 4
sum of them is 10
*/
#include <initializer_list>
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::initializer_list;
using std::string;

int sum(initializer_list<int> il) {
  int sum = 0;
  for (auto i : il) {
    cout << "got: " << i << endl;
  }
  cout << "total size: " << il.size() << endl;
  for (auto it = il.begin(); it != il.end(); it++) {
    sum += *it;
  }
  return sum;
}

int main() {
  cout << "sum of them is " << sum({1, 2, 3, 4}) << endl;
}

Please refer to this link and try yourself.

The positive is you can use it easily in construction function, and gather results like a vector. The negative is it is pretty hard to pass a set of args in different type.

3. template

Template is a pretty useful feature in C++.

In this way, we can create a few functions, and compiler will automatically create related functions in compile time.

Here is a small example using template parameters:

/* output:
accept: add type int, value1
accept: add type double, value2
accept: add type string, value abc=>3
sum of them is 6
*/
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;

class S {
 public:
  S() : sum_(0) {}

  template <class... Args>
  S(const Args &... args) : sum_(0) {
    Add(args...);
  }

  S &Add(int i) {
    sum_ += static_cast<double>(i);
    cout << "accept: add type int, value" << i << endl;
    return *this;
  }

  S &Add(double i) {
    sum_ += i;
    cout << "accept: add type double, value" << i << endl;
    return *this;
  }

  S &Add(const string &s) {
    sum_ += s.length();
    cout << "accept: add type string, value "
         << s << "=>" << s.length() << endl;
    return *this;
  }

  template <class First, class... Rest>
  S &Add(const First &first, const Rest &... rest) {
    return Add(first).Add(rest...);
  }

  double val() { return sum_; }

 private:
  double sum_;
};

int main() {
  S s(1, 2.0, string("abc"));
  cout << "sum of them is " << s.val() << endl;
}

Please refer to this link and try yourself.

The positive is you can handle a lot of certain situation, and will quickly failed in compile time, the negative is it really spend a lot of time in coding.

If you are interested, you can also visit this link for one of my applications.

来自的你,很高兴你能看到这儿。若本文对你有所用处,或者内容有什么不足之处,敬请毫不犹豫给个回复。谢谢!