Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top