1

So I am trying to make 4 template functions that do the following: sum a generic container, sum a map, sum generic container iterators, and sum generic map iterators. I have managed to do the first three, but I can't figure out the sum map iterators. Here is my code:

Sum.hpp

#ifndef SUM_HPP
#define SUM_HPP

#include <iostream>
#include <map>
#include <string>
using namespace std;

//Sum Generic Container
template <typename T>
double Sum(const T& cont) {
    double sum = 0; 

    typename T::const_iterator iter = cont.begin();
    for (iter; iter != cont.end(); iter++) {
        sum += *iter;
    }

return sum;
}

//Sum Generic Map
template <typename T1, typename T2>
double Sum(const map<T1, T2>& mp) {
    double sum = 0;

    typename map<T1,T2>::const_iterator iter = mp.begin();
    for (iter; iter != mp.end(); iter++) {
        sum += iter->second;
    }

    return sum;
}

//Sum Generic Container Iterators
template <typename T>
double Sum(T& begin, const T& end) {
    double sum = 0;

    for (begin; begin != end; begin++) {
        sum += *begin;
    }

    return sum;
}

//Sum Generic Map Iterators
template <typename T1, typename T2>
double Sum(map<T1, T2>::iterator& begin, map<T1, T2>::iterator& end) {
    double sum = 0;

    for (begin; begin != end; begin++) {
        sum += begin->second;
    }

    return sum;
}


#endif  

Test.cpp:

#include "Sum.hpp"
#include <iostream>
#include <map>
using namespace std;

int main() {
    //Map
    cout << "map" << endl;
    map<string, double> mp;

    mp["weight"] = 5.5;
    mp["height"] = 6.7;
    mp["length"] = 8.4;

    map<string, double>::iterator mp_iter_begin = mp.begin();
    map<string, double>::iterator mp_iter_end = mp.end();
    cout << Sum(mp_iter_begin, mp_iter_end) << endl;

    return 0;
}

When I run the Sum() function it tries to call the Sum Generic Container Iterators function, if I comment out the Sum Generic Container Iterators function, I get a "no instance of overloaded function" error. Can anyone spot what I am doing incorrectly?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
EliSquared
  • 1,409
  • 5
  • 20
  • 44
  • Possible duplicate of [Why is the template argument deduction not working here?](https://stackoverflow.com/questions/1268504/why-is-the-template-argument-deduction-not-working-here) – Davis Herring Oct 17 '17 at 00:38

2 Answers2

2

This belongs to non-deduced contexts:

1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

That means for map<T1, T2>::iterator, the template parameter T1 and T2 can't be deduced.

You can solve the issue via SFINAE, e.g.

//Sum Generic Container Iterators
template <typename T>
auto Sum(T begin, T end) -> remove_reference_t<decltype(*begin += *begin)> {

    remove_reference_t<decltype(*begin)> sum = 0;

    for (; begin != end; begin++) {
        sum += *begin;
    }

    return sum;
}

//Sum Generic Map Iterators
template <typename T>
auto Sum(T begin, T end) -> remove_reference_t<decltype(begin->second += begin->second)> {

    remove_reference_t<decltype(begin->second)> sum = 0;

    for (; begin != end; begin++) {
        sum += begin->second;
    }

    return sum;
}

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks for the help. I am aware that this is a solution, but I guess I should have specified I need to do this via specialization. I posted an answer below that does the trick. – EliSquared Oct 17 '17 at 01:25
  • 1
    @EliSquared But your solution only works with `map`, but not `map`, `map` etc; you have to write the specializations for every possible types. – songyuanyao Oct 17 '17 at 01:29
  • Yes that is true, but this is a homework assignment so I have to stay within the confines of what is asked. Your solution is better for the real world. – EliSquared Oct 17 '17 at 01:37
0

I figured it out how to do it the way I wanted to with specialization:

//Sum Generic Container Iterators
template <typename T>
double Sum(T& begin, const T& end) {
    double sum = 0;

    for (begin; begin != end; begin++) {
        sum += *begin;
    }

    return sum;
}

//Sum Map<string, double> Iterators
template <>
double Sum(map<string, double>::iterator& begin, const map<string, double>::iterator& end) {
    double sum = 0;

    for (begin; begin != end; begin++) {
        sum += begin->second;
    }

    return sum;
}

Note the importance of making sure the generic container iterator and map iterator function having matching arguments (both arguments are references and the second argument is a const for both template functions) or the specialization for map<string,double> will not work.

EliSquared
  • 1,409
  • 5
  • 20
  • 44
  • 1
    Note that as partial specialization is not allowed for function, you cannot handle `map::iterator`. You have to list each combination that you want to support (and allocator...). – Jarod42 Oct 17 '17 at 08:01
  • In addition, there are the `const_iterator` version too. – Jarod42 Oct 17 '17 at 08:02
  • And a more complicated part: `map::iterator` is not fully specified, so it might be possible that `map::iterator` and `map::iterator` are same type. – Jarod42 Oct 17 '17 at 08:05
  • None of the standard library algorithms modify the iterators passed to them, so your functions will surprise users. For example, `Sum(m.begin(), m.end())` won't compile at all. And if I happened to pass named iterators like `Sum(i, j)`, the fact that `i` has changed could be even more surprising. – aschepler Oct 24 '17 at 02:41
  • @aschepler This is just an academic exercise and not production code. Additionally, I had trouble finding a C++ example anywhere online that clearly demonstrated how to use specialization for my use case. If you have a way that can perform the sum on the map that is generalizable and doesn't require lambda expressions or use the word auto, I would be interested. – EliSquared Oct 27 '17 at 02:26
  • Well, specializing function templates is almost never actually useful. Just plain overloading is more straightforward and more flexible. – aschepler Oct 27 '17 at 22:41