Reputation: 9
template <class RandomIterator, class T>
T median(RandomIterator b, RandomIterator e)
{
std::sort(b, e);
int count = 0;
for(RandomIterator ri = b; ri != e; ++ri)
++count;
int mid = count / 2;
return (count % 2 == 0) ? *(b+mid) : (*(b+mid) + *(b+mid+1)) / 2;
}
I'm having trouble with class T, the return type for the median function. If I take it out and turn T into int, it seems to work but lessens the meaning of using templates. Help!
Upvotes: 0
Views: 64
Reputation: 308
To answer your actual question, the issue with templates is that the compiler needs to be able to figure out what the type T
is just from the function call. It can't do this because T
is never used directly, and certainly not in the function signature (which excludes the return type).
One approach is to explicitly state it when calling, e.g. median<std::vector<double>::iterator, double>(mvvec.begin(), myvec.end());
but that is rather unwieldy. A better option would be to swap the order of RandomIterator
and T
in the template declaration so that you can just specify the return type: median<double>(myvec.begin(), myvec.end());
However, in C++11 and later you can do better. Drop the class T
in the template and use auto, specifying the iterator's value_type if needed:
auto median(RandomIterator b, RandomIterator e) -> decltype(*b)
You may find the decltype is not necessary. However you also need to be sure whether you want to return a a value or a reference - there are pros and cons each way so I can't decide for you.
However, your function is unusual in that it both operates on a range (pair of iterators) yet returns a value. Most STL algorithms return an iterator, because they can't be sure if it is safe to dereference it or not. Suppose you passed begin() and end() from an empty vector, for example. By returning an iterator the caller makes the decision to dereference (or not). This also solves the problem of returning a value or reference. The call is then just median(mvvec.begin(), myvec.end());
- add a deference if you need to.
b
and e
must be random access iterators since you are calling to std::sort
. However the count is calculated inefficiently. Consider simply using auto count = e - b;
and auto mid = count / 2;
Use of auto
will give you the correct difference_type, which is not always the same as int. Typically it is ptrdiff_t but good iterator code shouldn't assume even that.
If you can't use auto
then typename std::iterator_traits<RandomIterator>::difference_type
is the correct type to use.
Upvotes: 1
Reputation: 8475
Switch the order of template parameters:
template <class T, class RandomIterator>
T median(RandomIterator b, RandomIterator e)
This way you need to pass only the type of T at the call:
median<int>(it1, it2);
Or, better yet, get rid of T altogether:
template <class RandomIterator>
auto median(RandomIterator b, RandomIterator e) -> auto(*b)
Upvotes: 2
Reputation: 7482
There is no need to specify the return type as template argument. You can access the underlying type of an iterator using the following:
std::iterator_traits<RandomIterator>::value_type
If you still want to do that, then you have to specify the type of all template arguments explicitly because the compiler is not able to uniquely infer it by using the other types provided.
I would remove it altogether as in the following:
template <class RandomIterator>
std::iterator_traits<RandomIterator>::value_type median(RandomIterator b, RandomIterator e)
or even better using auto
template <class RandomIterator>
auto median(RandomIterator b, RandomIterator e)
Upvotes: 0