0

I am trying to create a general sum function template. This template will be left associative. Below is my implementation

    int result=0;
template <typename D, typename T>
const T &sum_helper(const D &d, const T &v) {
    result=result+v;
    return result;
}
int pass(...){}

template <typename D, typename T1, typename... Ts>
auto sum_helper(const D &d, const T1 &v1, const Ts &... params) -> decltype(v1 + sum_helper(d, params...)) {
    return v1 + sum_helper(d, params... );
}

class A {};

template <typename... Ns> 
struct seq {};

template <typename... Ts>
auto sum(const Ts &... params) -> decltype(sum_helper(A(), params...)) 
{
    return  pass((sum_helper(seq<Ts...>(),params)) ...,1);

}

But when I call it like sum(1,2,3,4) it outputs 0 always. Whats wrong? I know that pass should be corrected. But what is the way to correct it?

footy
  • 5,803
  • 13
  • 48
  • 96

3 Answers3

3

Here's a simpler solution:

#include <iostream>

using namespace std;

template <typename T1>
auto _sum(T1 & _ret, const T1 & _t1) -> T1
{
    _ret += _t1;
    return _ret;
}

template <typename T1, typename... Ts>
auto _sum(T1 & _ret, const T1 & _t1, const Ts &... params) -> T1 
{
    _ret += _t1;
    return _sum(_ret, params...);
}

template <typename T1, typename... Ts>
auto sum(const T1 & _t1, const Ts &... params) -> T1 
{
    T1 ret = _t1;
    return _sum(ret, params...);    
}

int main()
{
    cout << sum(1, 2, 3, 4, 5) << endl;
    return 0;
}
Rollie
  • 4,391
  • 3
  • 33
  • 55
  • Thanks! Could you please explain how is this left associative? – footy May 10 '13 at 22:24
  • @footy: Because it calculates `1 + (2 + 3 + 4 + 5)`. Be aware that left-associativety does NOT fix the order of evaluation in general. – MSalters May 10 '13 at 22:37
  • I tried the following for objects after overloading + operator. It says `template argument deduction/substitution failed` .. How? I am not able to understand – footy May 11 '13 at 01:38
  • 1+(...) is right-associative. Left-associative would be (...)+5. – Matthias Benkard May 11 '13 at 09:41
  • @Rollie I needed left associativi – footy May 13 '13 at 01:01
  • Hmmm, that's a bit harder - updated code (assuming Matthias is correct re associativity) – Rollie May 13 '13 at 01:26
  • Did you intend to write `T1 ret = _t1;`? Because right now the first argument is lost. Also, it's no longer possible to call `sum(n)` with a single argument. And while you certainly fixed the associativity, I don't think it needed to be this complex. See my answer. – Ben Voigt May 13 '13 at 01:52
  • Ok, it did need to be complex, but for an entirely different reason. I see you sidestepped that by not using the exact return type, which may cause unexpected truncation with e.g. `sum(1, 2, 3.4)`. – Ben Voigt May 13 '13 at 03:21
3

Original answer doesn't work, because the trailing return type uses an overload prior to its point of declaration. It's impossible to forward declare a function before knowing its return type, also. So we need a helper struct. Here's the (unfortunately now very complicated) version that works:

#include <utility>

template <typename...>
struct sum_impl;

/* This is the base case */
template <typename T1, typename T2>
struct sum_impl<T1, T2>
{
    typedef decltype(std::declval<const T1&>() + std::declval<const T2&>()) result_type;

    static result_type doit(const T1& v1, const T2& v2)
    {
        return v1 + v2;
    }
};

/* And here is the recursive definition for left-associativity */
template <typename T1, typename T2, typename... Ts>
struct sum_impl<T1, T2, Ts...>
{
    typedef decltype(std::declval<const T1&>() + std::declval<const T2&>()) step_type;
    typedef typename sum_impl<step_type, Ts...>::result_type result_type;

    static result_type doit(const T1& v1, const T2& v2, const Ts&... rest)
    {
        return sum_impl<step_type, Ts...>::doit(v1 + v2, rest...);
    }
};

template <typename... Ts>
typename sum_impl<Ts...>::result_type sum(const Ts&... args)
{
    return sum_impl<Ts...>::doit(args...);
}

Demo: http://ideone.com/jMwgLz


Here's a version that retains the simplicity of Named's answer but is left associative:

/* not really needed, unless someone wants to call sum with only a single argument */
template <typename T>
T sum(const T& v)
{
    return v;
}

/* This is the base case */
template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2 )
{
    return v1 + v2;
}

/* And here is the recursive definition for left-associativity */
template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( sum(v1 + v2, rest...) )
{
    return sum(v1 + v2, rest... );
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Yeah, that looks better than mine :P – Rollie May 13 '13 at 02:06
  • @Ben this is awesome! Almost correct.. but if I give more than 4 arguments it seems to fail to compile – footy May 13 '13 at 02:19
  • @footy: [I see](http://ideone.com/eBq1Mc), it is because the trailing return type uses recursion, but it's before the point of declaration for the current overload. Don't the other answers fail similarly? – Ben Voigt May 13 '13 at 03:02
  • And, it looks like someone already figured this out: http://stackoverflow.com/questions/6065810/how-to-implement-folding-with-variadic-templates?rq=1 – Ben Voigt May 13 '13 at 03:25
1

But when I call it like sum(1,2,3,4) it outputs 0 always. Whats wrong?

That is because pass is not returning anything so what you have here is Undefined Behavior since you are flowing out of a non void function with out returning anything.

I don't know why you even needed pass here

return  pass((sum_helper(seq<Ts...>(),params)) ...,1);

You can just expand the variadic args and send them directly to sum_helper. like this

return  sum_helper(seq<Ts...>(),params...);


However a simpler version would be

template <typename T>
T sum(const T& v) {
    return v;
}

template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2) {
    return v1 + v2;
}

template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( v1 + v2 + sum(rest...) ) {
    return v1 + v2 + sum(rest... );
}

int main() {
    cout << sum(1,2,3,4);    
}

And an even simpler version is provided by Rollie's answer.

stardust
  • 5,918
  • 1
  • 18
  • 20
  • I tried the following for objects after overloading + operator. It says template argument deduction/substitution failed .. How? I am not able to understand – footy May 11 '13 at 01:48
  • I think your solution won't work for `sum(1,2,3)`; or any odd number of args; it will go to the variadic sum and call `1+2+sum(3)`, and `sum(3)` is not defined. – Rollie May 11 '13 at 02:40
  • @Rollie Yes. I have fixed it. The reason I chose two args in the long version of sum is because gcc doesn't see to be able to deduct `decltype( v1 + sum(rest...) )` properly for some reason. It matches it when there are two args matched and the rest expanded. – stardust May 11 '13 at 12:10
  • @footy What kind of error messages? and what kind of objects are you adding? – stardust May 11 '13 at 12:11