Reputation: 42379
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 function AreEqual() that accepts arbitrary number of arguments?
The trivial but tedious soultion is through overloading:
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 is:
bool AreEqual(...);
This solution is not workable, because the caller must add another argument (argument count or ending marker) to specify the number of the arguments.
Yet another way is through variadic template arguments
template<class... Args>
bool AreEqual(Args... args)
{
// What should be placed here ???
}
Upvotes: 6
Views: 623
Reputation: 20759
The varadic template requires recursion on specialization:
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 every time are_equal is nested, others... will short by one until estinguish, and the function will bind the two args.
Note:
By using A
as a type for both a
and b
, and because Others
's first has always to match an A, this in-fact- makes are_equal(...)
to accept all arguments only of a same type (ot at least, convertible into the type of the first argument).
Although I fond this constrain generally useful, the limitation can be relaxed by using A
and B
as types for a
and b
This makes the function to work with every set of types for which an operator==
exist 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...); }
Upvotes: 1
Reputation: 168866
Here is how one can implement it with 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 variations are appropriate for your use:
template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
return std::min({first==args...});
}
template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
for(auto i : {args...})
if(first != i)
return false;
return true;
}
Upvotes: 11
Reputation: 8143
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;
}
With optimizations on, there is no overhead compared to the recursive functions except that the behavior can be different since all arguments are compared to the first one.
Upvotes: 3
Reputation: 88225
Since you seem to be ruling out the sensible way to do it for some reason you could also try using std::initializer_list
:
template<typename T>
bool AreEqual(std::initializer_list<T> list) {
...
}
Then you'd call it like:
AreEqual({1,1,1,1,1});
Upvotes: 4
Reputation: 182875
Use
bool AreEqual(int v1, ...);
You will need, however, to detect the end of the list of integers somehow. If there's a particular integer value that is not legal to pass to the function, then use that. For example, if all integers are positive, you can use -1
to mark the end. Otherwise, you can make the first parameter a count.
Here's the count 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's the end 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;
}
Upvotes: 1