Für die Initialisierung brauchen wir das neutrale Element des Wertetyps bzgl. der Addition. Dieses kann ebenfalls über eine Typabbildung hergeleitet werden. Damit ist eine vereinfachte Version der Summe ohne init-Parameter möglich.

Das neutrale Element automatisch berechnen

Für die Fälle, in denen es ein eindeutiges neutrales Element (bzgl. der Addition) zum Wertetyp von Iterator gibt, können wir aber eine Vereinfachung anbieten, die gar kein init mehr braucht. Dazu müssen wir neben dem Wertetyp auch noch den Wert des neutralen Elements aus Iterator ableiten. Hier können wir analog zu value_type eine Typ-Abbildung neutral_element_addition definieren, die jetzt aber nicht Typ auf Typ, sondern Typ auf Wert abbildet. Sinnvollerweise ist neutral_element_addition nicht auf einem Iterator-Typ, sondern auf dem zugehörigen Werte-Typ definiert:

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(""); } };

Momementan haben wir nur für std::string eine Ausnahme (via Template-Spezialisierung) implementiert. Für Ihre eigene, blitzschnelle 3D-Vektor-Klasse vec3 müssen Sie dann ebenfalls ein solche Spezialisierung implementieren, falls vec3(0) nicht funktioniert:

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

Summe mit automatischem init

In der neuen Implementierung von sum2 holen wir uns dann das neutrale Element über neutral_element_addition, wobei wir im Code der Übersichtlichkeit halber einen typedef für den Werte-Typ spendieren:

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

Diese Variante kann parallel zu der allgemeineren Version mit explizitem init angeboten werden. Damit funktionieren dann endlich unsere Beispiele für string und float ohne irgendwelche Gymnastik:

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

Enge Wertetypen schlagen zurück

... nur dass die Ausgabe bei strings immer noch nicht gefällt. Außerdem begegnet uns ein altes Problem wieder: Mit der neuen Version, die das init-Argument automatisch berechnet, ist es viel einfacher, in die Falle eines zu engen Wertetyps zu tappen:

vector<unsigned char> dice_rolls(BigN); 

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

Wäre nur die Version (sum3) mit explizitem init Parameter verfügbar, dann hätten wir wenigstens eine sichtbare, explizite Angabe des (möglicherweise ungeeigneten) Wertetyps beim Aufruf erzwungen (und eine gewisse Chance, dass der Programmierer das richtige tut). Es ist eine sehr gute Übung, über Lösungen dieses Konflikts zwischen Sicherheit und Bequemlichkeit nachzudenken (auch und grade weil Sie damit den Rahmen diese Tutorials verlassen ...).