I am struggling with making a design choice in the following setup:
I am writing (C++) functions which take a pair of iterators (to template containers) and compute a return value of the same type that the iterators are pointing to.
Let's say I want to implement a sum function (this is just an example to illustrate the point).
The way I see it I have two options and both have a downside:
Option 1
Make the function template parameter T
defining the type the container stores.
I only need one parameter but I can only use the function on iterators of MyContainer
.
template <class T>
T sum(typename MyContainer<T>::IteratorRange range){
T sum;
for (auto it = range.first; it < range.second; ++it) {
sum += *it;
}
return sum;
}
Option 2
Have the function take an arbitrary IteratorRange
template class
and a second class U
determining the return type.
While the iterators can be sourced from any container, I need two template classes even though the return type is always the same type as pointed to by the iterators.
template <class IteratorRange, class U>
U sum(IteratorRange range){
U sum;
for (auto it = range.first; it < range.second; ++it) {
sum += *it;
}
return sum;
}
Which option is cleaner or is there an alternative offering the benefits of both options?
Bonus Question
How should one initialize the sum
variable?
Best Answer
The range to iterate over
The C++ standard library offers something of a canonical form here,
accumulate
;It takes a range from first to last (excluding last) and adds each element in the range to the
init
value as an initial value. It will also infer the return type from the initial type.If the an implementation of
sum
is required, I would advise the use ofaccumulate
here as is. If not suitable, the function signature taking the range [first, last) is "normal" as it mirror the standard library.Deduce the return type
To get the value type "behind" the iterator,
std::iterator_traits
, in particular the embedded typevalue_type
can be used;Design choice
I would design the function to (variation of option 2);
std::iterator_traits
(that work with pointers as well) to infer the return typeAs follows:
An alternative with reduced template arguments.