Usually, C++ standard library algorithms have two versions, e.g.:
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
Where one version is default in some sense and the second one is customized.
When I write my own algorithms, I use the following approach:
Let us suppose that I want to write a flexible algorithm that computes the average value. The standard C++ approach would be to write 2 algorithms:
template< class RandomIt >
double average( RandomIt first, RandomIt last );
template< class RandomIt, class TGetValue >
double average( RandomIt first, RandomIt last, TGetValue getValue );
where getValue
is something that obtains the value to be averaged from the object pointed to by the iterator.
But what if we have an iterator that loses some information when dereferenced? For example, let us consider std::map<TKey, TValue>::iterator
. This iterator results in a reference to a std::pair<TKey, TValue>
, but some iterators such as QMap::iterator return a reference to TValue
. That is, after we dereference QMap::iterator
, we lose the ability to examine the TKey
. Here some flexibility is lost:
QMap<int, double> map;
...
double averageOfKeys = average(map.begin(), map.end(), ???);
It is impossible to compute the average of keys using this interface, since the getValue
object expects the thing pointed to by the iterator, which has the type of the second template parameter. There is no way we can get the key, which is of type int
, from the value, which is of type double
.
But the iterator itself can be used to get both the key and the value, using the iterator::key()
and iterator::value()
member functions. All we need to do is to change the interpretation of the getValue
functor to expect an iterator instead of a thing pointed to by the iterator:
QMap<int, double> map;
...
using iterator = QMap<int, double>::const_iterator;
double averageOfKeys = average(map.begin(), map.end(),
[](iterator it){return it.key();});
So I made this template class:
template <class T>
class Dereference
{
public:
typedef decltype(*std::declval<T>()) TReference;
typedef typename std::remove_reference<TReference>::type TValue;
typedef typename std::remove_cv<TValue>::type TNonConstValue;
TReference operator()(const T& t) const {return *t;}
};
To serve as a default argument in my algorithms. So for example for the average
example, my algorithms would be:
template< typename RandomIt, typename TGetValue = Dereference<RandomIt> >
double average( RandomIt first,
RandomIt last,
TGetValue getValue = Dereference<RandomIt>() );
This increases the flexibility of the algorithm and reduces the need to have two overloads thanks to the default parameter.
Since I am new to programming in general and especially to C++11, I would like to ask:
- If this is intuitive and usable.
- If this can backfire in the future in some unexpected way.
- If this approach can be improved somehow.
QMap::begin()
ThatReturns an STL-style iterator pointing to the first item in the map.
SO why is it non standard. \$\endgroup\$