一段代码胜过千言万语:
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);
}
如何实现接受任意数量参数的函数AreEqual()?
琐碎但乏味的灵魂是通过超载:
bool AreEqual(int v1, int v2);
bool AreEqual(int v1, int v2, int v3);
bool AreEqual(int v1, int v2, int v3, int v4);
......
另一个微不足道但不可行的解决方案是:
bool AreEqual(...);
此解决方案不可行,因为调用者必须添加另一个参数(参数计数或结束标记)以指定参数的数量。
另一种方法是通过可变参数模板参数
template<class... Args>
bool AreEqual(Args... args)
{
// What should be placed here ???
}
答案 0 :(得分:11)
以下是如何使用模板实现它:
#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";
}
您应该考虑这些变化是否适合您的使用:
<小时/> 或者,这是一个非递归版本。可悲的是,它并不是短暂的。要查看非递归短路版本,请参阅另一个答案。
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;
}
答案 1 :(得分:4)
由于您出于某种原因似乎排除了合理的方式,您也可以尝试使用std::initializer_list
:
template<typename T>
bool AreEqual(std::initializer_list<T> list) {
...
}
然后你会这样称呼:
AreEqual({1,1,1,1,1});
答案 2 :(得分:3)
这是一个非递归版本:
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;
}
通过优化,与递归函数相比没有开销,除了行为可以不同,因为所有参数都与第一个进行比较。
答案 3 :(得分:1)
使用
bool AreEqual(int v1, ...);
但是,您需要以某种方式检测整数列表的结尾。如果有一个特定的整数值传递给函数是不合法的,那么使用它。例如,如果所有整数都是正数,则可以使用-1
标记结尾。否则,您可以将第一个参数设为计数。
这是计数版本:
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;
}
这是结束标记版本:
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;
}
答案 4 :(得分:1)
varadic模板需要专业化的递归:
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...); }
本质上,每次are_equal都是嵌套的,其他的......将短暂一直到estinguish,并且该函数将绑定两个args。
注意强>:
使用A
作为a
和b
的类型,并且因为Others
的第一个始终匹配A,这实际上使{{1}仅接受相同类型的所有参数(至少可以转换为第一个参数的类型)。
虽然我喜欢这种约束通常很有用,但使用are_equal(...)
和A
作为B
和a
的类型可以放宽限制
这使得该函数适用于每对类型都存在b
的每种类型。
operator==