1

I have the following sumhelper written:

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

Here is the CPP file

#include <iostream>
#include <type_traits>
#include "Sum.hpp" 

struct A {
    int x;

    A(const int a) : x(a) { std::cout<<x<<std::endl; };

    A &operator+(const A &tmp) const {
        std::cout<<" + "<<tmp.x<<") ";
    };
};

int main () {
    std::cout<<"sum of 1,2,3,4 is : ";

    auto ans = sum(1,2.2,3,4);

    A a(1);
    A b(2);
    A c(3);

    std::cout<<a.x;
    a+b+c;

    sum(a,b,c); //Here is syntax error

    std::cout<<ans<<std::endl;
    return 0;
}

Why am I not able to do the sum(a,b,c) ? When I have a+b+c working as demonstrate.

It gives a compile error when I pass objects but not when I pass primitive types

I am not able to understand the error template argument deduction/substitution failed.. how?

footy
  • 5,803
  • 13
  • 48
  • 96

3 Answers3

2

This is my bad (from previous answer) you should add one more template specialization at the top for a single element. Otherwise sum(1,2,3) will not find a match because the first two args will be matched by the variadic one and the other expects two args. There is only one left.

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

Here again another problem is your operator+ flows out with out returning anything. Which is Undefined Behavior. And if you define it as a member it should be const.

struct A {
    int x;
    A(const int a) : x(a) { std::cout<<x<<std::endl; };
    A operator+(const A &a1) const
    {
        return A(a1.x + x);
    }
};

So your complete program should now look like this

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

struct A {
    int x;
    A(const int a) : x(a) { };
    A operator+(const A &a1) const { return A(a1.x + x); }
};

int main () {
    std::cout<<"sum of 1,2.2,3,4 is : ";
    auto ans = sum(1,2.2,3,4);
    cout << ans;


    A a(1); A b(2); A c(3);
    a+b+c;
    auto ans2 = sum(a,b,c);
    std::cout<<std::endl<<"sum of A(1),A(2),A(3) is : ";
    std::cout<<ans2.x<<std::endl;
    return 0;
}

stdout

sum of 1,2.2,3,4 is : 10.2
sum of A(1),A(2),A(3) is : 6
stardust
  • 5,918
  • 1
  • 18
  • 20
  • You can do `sum` this with 2 overloads. – Yakk - Adam Nevraumont May 11 '13 at 13:52
  • @Yakk That's what I thought but gcc has issues with `decltype( v1 + sum(rest...) )`. Can not deduce the type of `sum(rest...)` with one arg. It works fine if you match two args explicitly like I did. Hold on I will show you what I mean. – stardust May 11 '13 at 13:54
  • @Yakk Here. http://coliru.stacked-crooked.com/view?id=b13ddc636e02fa459d0b82e125b6c820-7063104e283ed82d51a6fde7370c6e59 – stardust May 11 '13 at 14:01
  • Interesting. It doesn't seem to want to use itself to deduce its own return type? – Yakk - Adam Nevraumont May 11 '13 at 14:03
  • Yes. It doesn't seem to recognize the single arg sum when deducting type for `decltype( v1 + sum(rest...) )`. When `rest` is just one arg. – stardust May 11 '13 at 14:04
  • @Yakk Actually you are right. It doesn't consider **itself**. I did some testing for a similar question here http://stackoverflow.com/questions/16497977/general-min-and-max-auto-return-type – stardust May 11 '13 at 14:36
  • Oh, and as an aside, you should use perfect forwarding on `sum`. Little reason not to. :) – Yakk - Adam Nevraumont May 11 '13 at 14:39
  • This doesnt seems to work for `sum(a,b,c,d,e,f);` where the arguments are all of the type struct `A` in the question. – footy May 11 '13 at 23:16
  • @footy It is working for me. I don't why it didn't work for you. – stardust May 12 '13 at 11:40
2

Here is a variation of a variardic apply_binop that takes an arbitrary operation as the first argument, and a sum wrapper that passes a binary add to it. apply_binop is better (and more clearly) known as foldr I believe:

#include <utility>
#include <iostream>

#define RETURNS(x) ->decltype(x) { return (x); }

struct add {
  template<typename T, typename U>
  auto operator()( T&& t, U&& u ) const
    RETURNS( std::forward<T>(t)+std::forward<U>(u) )
};


template<typename Op, typename T0>
auto apply_binop( Op&& op, T0&& t0 )
  RETURNS(std::forward<T0>(t0))

template<typename Op, typename T0, typename T1, typename... Ts>
auto apply_binop( Op&& op, T0&& t0, T1&& t1, Ts&&... ts )
  RETURNS(
    op(
      std::forward<T0>(t0),
      apply_binop(op, std::forward<T1>(t1), std::forward<Ts>(ts)...)
    )
  )

template<typename... Ts>
auto sum( Ts&&... ts )
  RETURNS( apply_binop( add(), std::forward<Ts>(ts)... ) )

int main() {
  std::cout << sum(1,2,3,4,5) << "\n";
  std::cout << sum(1) << "\n";
  std::cout << sum(1,2) << "\n";
  std::cout << sum(1,2,3) << "\n";
  std::cout << sum(1,2,3,4) << "\n";
  std::cout << sum(1,2,3,4.7723) << "\n";
}

It is foldr because it applies the binary operation to the rightmost two, then takes that result and applies it with the 3rd last, etc. foldl does the same starting from the left.

The macro RETURNS makes up for the inability for C++ to deduce return types for single line functions (which I believe will be fixed in C++17). Getting gcc 4.7.2 to accept the above with only two apply_binop overrides took a bit of tweaking.

Implementing foldl without 3 or more overrides is a tad trickier.

Here is another answer where they discuss better ways to work around this issue:

How to implement folding with variadic templates

Community
  • 1
  • 1
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

Here is a correct variadic sum

#include <iostream>

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

    template <typename T, typename T2, typename ... Ts>
    T sum(const T & v, const T2 & w, const Ts & ... params) {
        return sum(w+v,params...);
    }
}

int main() {
    using namespace cs540;
    using namespace std;
    cout << sum(1.1,2,3,4,6,8,9,1.1) << endl;
}

You also need to mark your method operator+ as const

aaronman
  • 18,343
  • 7
  • 63
  • 78