C++ 如何使函数接受不使用f(…)的任意数量的参数?

C++ 如何使函数接受不使用f(…)的任意数量的参数?,c++,function,templates,c++11,variadic-templates,C++,Function,Templates,C++11,Variadic Templates,一段代码胜过千言万语: 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); AreEqua

一段代码胜过千言万语:

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 ???
}
模板
布尔值相等(Args…Args)
{
//这里应该放什么???
}
使用

但是,您需要以某种方式检测整数列表的结尾。如果有一个特定的整数值不能合法地传递给函数,则使用该整数值。例如,如果所有整数都为正,则可以使用
-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;
}

以下是如何使用模板实现它:

#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";
}
#包括
#包括
模板
bool AreEqual(T0 T0){返回真;}
模板
布尔值相等(T0 T0,T1 T1,Args…Args){
返回t0==t1&&AreEqual(t1,args…);
}
int main(){

std::coutvaradic模板需要专门化上的递归:

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...); }

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

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

以下是一个非递归版本:

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;
}
使用identity=T的模板;
模板
布尔相等(T优先,Args…Args)
{
bool tmp=真;
标识{tmp?tmp=first==args:true…};
返回tmp;
}

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

所说的“任意”是指“任意类型的整数(例如,前两个是
int
s,第三个是
long
)”还是“任意数量的同一类型的参数”?您可以将参数声明为
long
,这样您就可以以较低的精度传递int、short、long、chars、bools等任何参数。您为什么要避免使用
f(…)
?@bames53,此解决方案不可行,因为调用者必须传递一个参数来指定参数的数量。@ValekHalfHeart当然可以,但这将强制转换(可能不是什么大问题),并且可能会阻止该函数与自定义类整数类型(例如大整数)一起使用例如,@Rob的解决方案适用于定义了
运算符==
的每种类型。此解决方案不可行,因为调用者必须传递一个参数来指定参数的数量。@xmlmx:这是一个选项。另一个选项是在列表末尾使用一个标记。强制调用者附加一个标记会更改function接口。我同意。但既然您没有实现(否则,为什么要问这个问题?)没有代码依赖于接口。因此它很容易更改。我有一个实现,即重载解决方案。但我觉得它并不优雅,所以我问了这个问题。它们可能应该是
内联的
@Utaal:为什么?模板已经有一个ODR”异常“排序。@GManNickG我指的是性能/代码大小,不是重复的定义。如果我没有弄错的话,
AreEqual(1,4,1,1,1)
被扩展为5个非模板“函数”(第一个取5个参数,最后一个只取一个参数)。这意味着,如果没有内联,您只需计算5个整数的相等值,就需要支付相当大的5个函数调用开销。我还不能确认这一点,但感觉编译器应该能够内联所有这些调用。如果您的编译器很好,它将生成内联代码(或不生成内联代码)完全独立于是否使用
inline
关键字。@Robᵩ, @GManNickG-哦,我在谷歌上搜索了一下,才意识到我一直在使用
inline
(大部分时间)错误的原因。我知道
内联
经常被忽略,但我不知道非
内联
函数是内联的。我认为这个解决方案是最好的。简洁、优雅、漂亮!这个答案应该得到更多的支持。+1启用了出色的实现
返回std::nexture_find(list.begin(),list.end()),std::not_equal_to())==list.end();
返回std::all_of(std::next(list.begin()),list.end(),std::bind(std::equal_to(),*list.begin(),std::占位符::_1));
(由于对第一个元素的特殊处理,它不太好看,但是正确地使用了
=
而不是
!=
,尽管
=
无论如何都应该与
!=
同步)。
template<typename T, typename... Args>
bool AreEqual(T first, Args... args)
{
  for(auto i : {args...})
    if(first != i)
      return false;
  return true;
}
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...); }
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...); }
template<typename T>
bool AreEqual(std::initializer_list<T> list) {
    ...
}
AreEqual({1,1,1,1,1});
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;
}