【问题标题】:Addition of variable number of numbers添加可变数量的数字
【发布时间】:2014-09-14 07:34:32
【问题描述】:

我正在尝试编写一个函数,它接受不同数据类型的多个参数并返回数字的总和。它应该能够决定用于求和的数据类型。例如 如果我写 add(3,1,2,3) 它应该将 sum 作为 int 返回。但是,如果我写 add(3,1,2.5,3.25) 它应该将 sum 作为双精度返回。

我尝试使用模板,但编译时出错。这是函数

template <typename T>
T add(int n, ...)
{
    T sum = 0;
    va_list vl;
    va_start(vl,n);
    for(int i=0;i<n;i++)
    {
        sum += va_arg(vl,T);
    }
    va_end(vl);
    return sum;
}
int main()
{
    std::cout<<add(3,1,2,3);
    return 0;
}

编译错误:没有匹配函数调用'add(int, int, int, int)'。 我认为错误即将到来,因为我传入了 va_arg T,但我不知道还要通过什么来保持它的概括性。

【问题讨论】:

  • 你为什么不用Varidaic Templates
  • 当传递的参数是整数时,这确实解决了问题,但是当传递双精度值时,它给出了错误的答案。
  • @DarshilBabel 不,它有效。 See it live
  • 它也适用于双倍live example here
  • 编译错误是因为调用add时没有指定typename。如果你想让它工作,你应该打电话给add&lt;int&gt;(3,1,2,3)。当然,这几乎结束了您在运行时确定返回类型的初始目标。从逻辑上考虑,如果不使用多态性,就没有办法做到这一点(在运行时确定返回类型)。

标签: c++


【解决方案1】:

你应该替换

std::cout<<add(3,1,2,3);

std::cout<<add<int>(3,1,2,3);  // explicitly, int, double, float whatever

由于编译器无法从隐式调用中推断出类型名,因此使代码成功运行。 Live code example here

你的函数返回类型为 T 的值,但你没有指定任何类型为 T 的参数,导致编译器无法推断类型。


其他解决方案是添加适用于add(0) 的重载,并在T 类型的add 中传递第一个参数。这样就可以实现OP的隐式演绎目标。现在编译器可以从第一个参数推断返回类型。

#include <cstdarg>
#include <iostream>
#include <cassert>
using namespace std;

int add(int n)
{
    assert(0 == n);
    return 0;
}

template <typename T>
T add(int n, T first, ...)
{
    T sum = first;
    va_list vl;
    va_start(vl,first);
    for(int i=1;i<n;i++)
    {
        sum += va_arg(vl,T);
    }
    va_end(vl);
    return sum;
}
int main()
{
    std::cout<<add(3,1,2,3);
    std::cout<<add(3,1.5,2.,3.5);
    return 0;
}

live code here

【讨论】:

  • 正如我在对 OP 问题的评论中提到的那样,这几乎结束了在运行时确定返回类型的初始目标。从逻辑上考虑,如果不使用多态性,就没有办法做到这一点(在运行时确定返回类型)。
  • 一种解决方法是纠正适用于 add(0) 的重载,并在类型 T 的 add 中传递第一个参数。这可以实现 OP 的隐式推导目标。
  • 你的意思是write(不是right)...而且我不明白你的建议是如何工作的。如果您认为确实如此,那么您可能希望将其添加到答案中。
  • 对不起,是我的错误(一开始没有注意到全局 add 函数......但看到它在所有情况下都能正常工作,我会感到非常惊讶。如果 OP 通过怎么办第一个参数为double?它会返回正确的结果吗?
  • 你知道吗?忘记第一个参数问题。我刚刚意识到您的函数在任何情况下都会返回int。如果正确的结果(参数的总和)不是整数怎么办?
【解决方案2】:

为什么不使用Varidaic Templates? – Mohit Jain

如果你可以用 C++14 编写:

template<typename T>
inline T sum(T t){
    return t;
}

template<typename T, typename... Ts>
inline auto sum(T t, Ts... ts){
    return t+sum(ts...);
}

#include<iostream>
int main(){
    std::cout<<sum(2.5, 2)<<'\n'
             <<sum(2, 2.5)<<'\n'
             <<sum(1u, 2.5, 3.f, '0')<<'\n';
}

如果你在 MSVC++ (it doesn't work under g++) 下用 C++11 编写:

template<typename T>
inline T sum(T t){
    return t;
}

template<typename T, typename... Ts>
inline auto sum(T t, Ts... ts)->decltype(t+sum(ts...)){
    return t+sum(ts...);
}

#include<iostream>
int main(){
    std::cout<<sum(2.5, 2)<<'\n'            //works
             <<sum(2, 2.5)<<'\n'            //works
             <<sum(1u, 2.5, 3.f, '0')<<'\n';//works under VC++, but not under g++
}

【讨论】:

  • 您可以使用std::common_type。 (并且首先强制提升可能会避免sum(int(42), char(127), char(127), char(2)) 的一些奇怪结果(42而不是298)。
  • 也许写更好的代码而不是指出我的弱点?
  • 我发现它比我的效果更好,我不知道为什么,我也不知道你为什么不把它作为答案发布。
  • 这只是对您答案的评论/改进。边缘情况是int32_t+int32_tint32_t(溢出丢失),首先升级,你解决了这个问题(但我认为它可能会引入浮点数的其他问题:-/)。 (我使用char 的第一个例子更棘手,因为char + char 结果实际上是int + int(而std::common_type&lt;char,char&gt;::typechar))。我不知道 OP/你想如何处理这些边缘情况。
【解决方案3】:

我认为问题在于您将参数类型确定为 int 尝试使用

代替:T Add (int n,...)

使用:T Add(T n, ...)

【讨论】:

  • 我猜n 是计数
  • n 是第一个参数名称
  • @MohitJain 我怀疑是这样,如果你走可变参数模板路线,这没什么意义,因为你可以通过sizeof... 免费获得它,如果有必要,还需要一点数学。
  • 你的意思是n是参数计数吗??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2015-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多