For initialization, we need the neutral element of the value type wrt. addition, which can be deduced via a typ map. This permits to implement a convenience version of sum without explicit init parameter.

Computing the neutral element

For those cases with a unique neutral element (wrt. addition) we can offer a simplification which does not need init any more. To implement this, we need to deduce, in addition to the value type, also the value of the neutral element from Iterator. In analogy to value_type we can define a type mapping neutral_element_addition, which now does not map type to type but type to value. It is also more natural to define this map directly on the value type instead of the iterator type. As normally, the neutral element can be initialized with the literal 0, we use this as default definition in the primary template.

template<typename T>
struct neutral_element_addition
{ static T value() { return T(0); } };

template<>
struct neutral_element_addition<std::string> 
{ static std::string value() { return std::string(""); } };

In the example above, only for std::string an exception (via template specialization) was defined. For your own, blazingly fast 3D vector class vec3 you have to implement a similar specialization, if vec3(0) does not work:

template<>
struct neutral_element_addition<vec3>
{ static vec3 value() { return vec3(0.0,0.0,0.0); } };

Sum with automatic init

In the new implementation of sum2 we now get the neutral element via neutral_element_addition, spending a typedef for clarity:

template<typename Iterator>
typename value_type<Iterator>::result
sum2b(Iterator  a, Iterator a_end)
{
  typedef typename value_type<Iterator>::result value_t;
  value_t res = neutral_element_addition<value_t>::value();
  for(; a != a_end; ++a)
    res += *a;
  return res;
}

We can offer this comfortable overload alongside the general version with explicit init. So, finally our examples for string and float work without any tweaks at the calling site:

float a[] = { 0.0, 0.5, 1.0, 1.5 };
float suma = sum2b(a, a+4); // beware of long sequences!

std::string words[] = { "This", "is", "a", "sentence" };
std::string sentence = sum2b(words, words+4);

Narrow value types hit again

... just the output for strings is not as pleasant. And an old problem is striking back: Automatic computation of the init argument makes it more likely to fall into the trap of a too narrow value type:

vector<unsigned char> dice_rolls(BigN); 

// surprise: unsigned char used for summing
unsigned sum_dice_rolls = sum2b(dice_rolls.begin(), dice_rolls.end());

When only the version (sum3) with explicit init parameter is available, there is at least a visible, explicit mention of that inadequate type at the calling site (and a chance of the programmer choosing a better type for init). It is a good excercise to think about if and how this could be resolved (and you'll leave the scope of this tutorial when doing so).