Home > Software engineering >  Enable_if problems
Enable_if problems

Time:09-22

I want to use the SFINAE principle to check whether the instantiated template is a random access iterator. To do this, I used std::enable_if, but the compiler throws an error:

Cannot combine with previous 'type-name' declaration specifier

How can I fix it? I really need help to figure it out.

template<class RandomAccessIterator, class Compare>
typename std::enable_if<std::is_same<std::random_access_iterator_tag, RandomAccessIterator>::value>
void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) {
    sorting(first, last, 0, last - first - 1, compare);
}

CodePudding user response:

Two issues I see:

  • You're writing std::enable_if<> instead of std::enable_if<>::type, which is probably not what you meant to do.

  • (The cause of the error) You have two return types: std::enable_if<> and void. You can only have one return type.

std::enable_if has a second optional type parameter that is the the type of std::enable_if<>::type. This is set by default to void so your return type will already be void from std::enable_if<>::type. However since you're putting enable_if in the return type I'd recommend you explicitly specify that the return type be void.

So the solution to your issue would look something like this:

template <class RandomAccessIterator, class Compare>
typename std::enable_if<
    std::is_same<std::random_access_iterator_tag, RandomAccessIterator>::value,
    void
>::type quick_sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) {
    sorting(first, last, 0, last - first - 1, compare);
}

Alternatively you could use std::enable_if in a template parameter:

template <
    class RandomAccessIterator,
    class Compare,
    typename std::enable_if<
        std::is_same<std::random_access_iterator_tag, RandomAccessIterator>::value,
        std::nullptr_t
    >::type = nullptr
>
void quick_sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) {
    sorting(first, last, 0, last - first - 1, compare);
}

CodePudding user response:

In addition to the technical issues mentioned in this answer, your approach will simply not work at all.

std::is_same<std::random_access_iterator_tag, RandomAccessIterator>::value will never evaluate as true, because an iterator is not an iterator tag. You need to use tag dispatching via std::iterator_traits, not SFINAE, to determine if the iterators being passed in are random-access or not.

See the example on cppreference: https://en.cppreference.com/w/cpp/iterator/iterator_tags

Try this instead:

template<class Iter, class Compare>
void quick_sort_impl(Iter first, Iter last, Compare compare, std::random_access_iterator_tag) {
    sorting(first, last, 0, last - first - 1, compare);
}

template<class Iter, class Compare>
void quick_sort(Iter first, Iter last, Compare compare) {
    quick_sort_impl(first, last, compare, typename std::iterator_traits<Iter>::iterator_category());
}

This way, if you try to call quick_sort() with iterators that are not random access, you will get a compiler error due to a missing quick_sort_impl() overload for non-random iterator tags. If you want to implement sorting for other types of iterators, simply overload quick_sort_impl() for the other types of iterator tags as needed.

  • Related