6

一段代码值一千字:

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 ???
}
4

5 回答 5

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;
}
于 2013-01-29T20:55:28.297 回答
4

由于您似乎出于某种原因排除了明智的做法,您也可以尝试使用std::initializer_list

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

然后你会这样称呼它:

AreEqual({1,1,1,1,1});
于 2013-01-29T21:00:08.253 回答
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;
}

启用优化后,与递归函数相比没有任何开销,只是行为可能不同,因为所有参数都与第一个参数进行比较。

于 2013-01-29T21:04:58.160 回答
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;
}
于 2013-01-29T20:50:17.407 回答
1

可变参数模板需要对特化进行递归:

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 嵌套时,其他人...都会缩短 1 直到 estinguish,并且该函数将绑定两个 args。

注意:通过将andA都用作类型,并且因为's first 总是与 A 匹配,这实际上使得只接受相同类型的所有参数(至少,可转换为 first 的类型争论)。abOthersare_equal(...)

尽管我喜欢这个约束通常很有用,但可以通过使用AandB作为 and 的类型来放松限制ab 这使得该函数可以处理每对operator==存在的每组类型。

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...); }
于 2013-01-29T20:58:43.753 回答