简体   繁体   中英

I'm having problems relating to template arguments

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!

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) 

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

Slight improvement

One approach is to explicitly state it when calling, eg 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());

Better improvement

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 aa value or a reference - there are pros and cons each way so I can't decide for you.

Best approach

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.

Algorithm refinement

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.

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM