5

I am trying to write a function which accepts multiple arguments of different data types and return the sum of numbers. It should be able to decide what data type to be used for sum. For e.g. if I write add(3,1,2,3) it should return sum as an int. However, if I write add(3,1,2.5,3.25) it should return sum as a double.

I tried using template, but giving compile time error. Here is the function

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

Compile Error: no matching function for call to 'add(int, int, int, int)'. I think error is coming because I passed in va_arg T, but i don't know what else to pass to keep it generalised.

Darshil Babel
  • 145
  • 2
  • 13
  • 4
    Why don't you use [Varidaic Templates](http://www.cplusplus.com/articles/EhvU7k9E/)? – Mohit Jain Sep 14 '14 at 07:39
  • That does solve the problem when passed arguments are integers, but when passing the double values, it is giving wrong answer. – Darshil Babel Sep 14 '14 at 07:41
  • @DarshilBabel no, it works. [**See it live**](http://ideone.com/ggLxjO) – WhozCraig Sep 14 '14 at 07:47
  • It works for double also [live example here](http://ideone.com/veyyZ6) – Mohit Jain Sep 14 '14 at 07:48
  • 1
    The compilation error is because you are not specifying the `typename` when you call `add`. You should call `add(3,1,2,3)` if you want to make it work. Of course, this pretty much puts an end to your initial goal of determining the return-type during runtime. Thinking about this logically, there is no way to do it (determine the return-type during runtime) without using polymorphism. – barak manos Sep 14 '14 at 07:53
  • I concur with barek now that I see you seem to need a "largest-represenation" solution that is pummeled through the variadic as it is determined. – WhozCraig Sep 14 '14 at 07:59
  • @WhozCraig: I need to use Google Translate on your comment (concur? pummeled?). – barak manos Sep 14 '14 at 08:01

3 Answers3

2

You should replace

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

with

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

To make the code run succesfully as the compiler is unable to deduce the typename from implicit call. Live code example here

Your function returns the value of type T, but you don't specify any argument of type T, making it impossible for compiler to deduce the type.


Other solution would be to add an overload that works for add(0) and pass first argument in add of type T. This can achieve OP's implicit deduction goal. Now compiler can deduce return type from first argument.

#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

Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
  • As I've mentioned in a comment to OP's question, this pretty much puts an end to the initial goal of determining the return-type during runtime. Thinking about this logically, there is no way to do it (determine the return-type during runtime) without using polymorphism. – barak manos Sep 14 '14 at 07:56
  • One workaround would be to right an overload that works for add(0) and pass first argument in add of type T. This can achieve OP's implicit deduction goal. – Mohit Jain Sep 14 '14 at 07:58
  • You mean *write* (not *right*)... And I don't see how your suggestion works. If you think it does, then you might want to add it to the answer. – barak manos Sep 14 '14 at 07:59
  • Sorry, was my mistake (didn't notice the global `add` function at the beginning... But I'd be very surprised to see this working properly in all cases. What if OP passes the first argument as `double`? Will it return the correct result? – barak manos Sep 14 '14 at 08:06
  • You know what? Forget the first argument issue. I've just realized that your function returns an `int` in any case. What if the correct result (the sum of the arguments) is not an integer? – barak manos Sep 14 '14 at 08:08
  • @Mohit What if first argument is integer and rest others are double. Then it will give the wrong answer. – Darshil Babel Sep 14 '14 at 09:14
  • @DarshilBabel this is the downside of using c style varargs. Using C++, you can write an intelligent piece of template to give a compile time error for such descrepencies. This is analogous to problem: what happens to printf if placeholder argument for %d is a double. – Mohit Jain Sep 14 '14 at 09:33
2

Why don't you use Varidaic Templates? – Mohit Jain

If you can write in 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';
}

If you write in C++11 under MSVC++ (it doesn't work under g++):

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++
}
Community
  • 1
  • 1
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
  • 1
    You may use `std::common_type`. (and forcing the promotion at first may avoid some *strange* result for `sum(int(42), char(127), char(127), char(2))` (42 instead of 298)). – Jarod42 Sep 14 '14 at 13:55
  • Only [when used `std::common_type` got `42` instead of `298`](http://rextester.com/QNCHB76405). – GingerPlusPlus Sep 14 '14 at 14:13
  • Maybe write better code instead of pointing out mine's weaknesses? – GingerPlusPlus Sep 14 '14 at 14:21
  • I see it works better than mine, I have no idea why, and I have no idea why you didn't posted it as answer. – GingerPlusPlus Sep 14 '14 at 14:31
  • It is just an remark/improvement of your answer. The edge case is that `int32_t+int32_t` is an `int32_t` (and the overflow is lost), with promotion first, you fix this issue (But I think it may introduce other issue with floating number :-/). (My first example with `char` is more tricky as `char + char` result in fact as `int + int` (and `std::common_type::type` is `char`)). I don't know how OP/you want to treat these edge case. – Jarod42 Sep 14 '14 at 14:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/61207/discussion-between-gingerplusplus-and-jarod42). – GingerPlusPlus Sep 14 '14 at 14:57
-2

i think the problem is you determined the parameter type as int try to use

instead of: T Add (int n,...)

use: T Add( T n, ...)

Z.a.A.h
  • 1
  • 4