How to make a function accept an arbitrary number of arguments without using f (...)?

A piece of code is worth a thousand words:

int main() { // All of the following calls return true: AreEqual(1, 1); AreEqual(1, 1, 1); AreEqual(1, 1, 1, 1); AreEqual(1, 1, 1, 1, 1); // All of the following calls return false: AreEqual(1, 2); AreEqual(1, 2, 1); AreEqual(1, 7, 3, 1); AreEqual(1, 4, 1, 1, 1); } 

How to implement the AreEqual () function, which takes an arbitrary number of arguments?

A trivial but tiring soul is an overload:

 bool AreEqual(int v1, int v2); bool AreEqual(int v1, int v2, int v3); bool AreEqual(int v1, int v2, int v3, int v4); ...... 

Another trivial but not workable solution:

 bool AreEqual(...); 

This solution does not work because the caller must add another argument (argument count or trailing token) to indicate the number of arguments.

Another way is to use the template's variational arguments

 template<class... Args> bool AreEqual(Args... args) { // What should be placed here ??? } 
+6
source share
5 answers

Here's how to implement it using templates:

 #include <iostream> #include <iomanip> template<class T0> bool AreEqual(T0 t0) { return true; } template<class T0, class T1, class... Args> bool AreEqual(T0 t0, T1 t1, Args ... args) { return t0 == t1 && AreEqual(t1, args...); } int main () { std::cout << std::boolalpha; // All of the following calls return true: std::cout<< AreEqual(1, 1) << "\n"; std::cout<< AreEqual(1, 1, 1) << "\n"; std::cout<< AreEqual(1, 1, 1, 1) << "\n"; std::cout<< AreEqual(1, 1, 1, 1, 1) << "\n\n"; // All of the following calls return false: std::cout<< AreEqual(1, 2) << "\n"; std::cout<< AreEqual(1, 2, 1) << "\n"; std::cout<< AreEqual(1, 7, 3, 1) << "\n"; std::cout<< AreEqual(1, 4, 1, 1, 1) << "\n"; } 

You should consider whether these options are suitable for your use:

  • Use links instead of passing by value
  • Make an invariant template with two parameters instead of one.


In addition, a non-recursive version is provided here. Unfortunately, this is not a short circuit. To see a non-recursive short-circuited version, see Another answer.
 template<typename T, typename... Args> bool AreEqual(T first, Args... args) { return std::min({first==args...}); } 


Finally, this version is attractive in its flaw:
 template<typename T, typename... Args> bool AreEqual(T first, Args... args) { for(auto i : {args...}) if(first != i) return false; return true; } 
+11
source

Since you seem to be eliminating a reasonable way to do this for some reason, you can also try using std::initializer_list :

 template<typename T> bool AreEqual(std::initializer_list<T> list) { ... } 

Then you will call it this:

 AreEqual({1,1,1,1,1}); 
+4
source

Here is a non-recursive version:

 template <typename T> using identity = T; template<typename T, typename... Args> bool AreEqual(T first, Args... args) { bool tmp = true; identity<bool[]>{tmp?tmp=first==args:true ...}; return tmp; } 

When using optimizations, there is no overhead compared to recursive functions, except that the behavior may be different, since all arguments are compared with the first.

+3
source

Using

  bool AreEqual(int v1, ...); 

However, you will need to somehow determine the end of the list of integers. If there is a specific integer value that is not legal for passing a function, use it. For example, if all integers are positive, you can use -1 to indicate the end. Otherwise, you can make the first parameter a number.

Here is the invoice version:

 bool AreEqual(int count, int v1, ...) { va_list vl; va_start(vl, count); for(int i = 1; i < count; ++i) if (va_arg(v1, int) != v1) { va_end(vl); return false; } va_end(vl); return true; } 

And here is the final marker version:

 bool AreEqual(int v1, ...) { va_list vl; va_start(vl, count); do { int param = va_arg(vl, int); if (param == -1) { va_end(vl); return true; } } while (param == v1); va_end(vl); return false; } 
+1
source

The varadic template requires specialization recursion:

 template<class A> //just two: this is trivial bool are_equal(const A& a, const A& b) { return a==b; } template<class A, class... Others> bool are_equal(const A& a, const A& b, const Others&... others) { return are_equal(a,b) && are_equal(b,others...); } 

In essence, is_equal is nested every time, the others ... will be short by one until they are defined, and the function will link the two arguments.

Note : Using A as the type for A and b , but because Others must always match A first, it actually does are_equal(...) to accept all arguments of only one type (at least converts to the type of the first argument) .

Although I like this delimiter, it is generally useful, the restriction can be relaxed by using A and b as types for A and b This makes the function work with every set of types for which operator== exists for each of its pairs.

 template<class A, class B> //just two: this is trivial bool are_equal(const A& a, const B& b) { return a==b; } template<class A, class B, class... Others> bool are_equal(const A& a, const B& b, const Others&... others) { return are_equal(a,b) && are_equal(b,others...); } 
+1
source

All Articles