1

Are there any differences between

template <typename ForwardIter, 
    typename Comp = std::less<typename std::iterator_traits<ForwardIter>::value_type>>
void select_sort(ForwardIter first, ForwardIter last, Comp comp = Comp())
{
    for (; first != last; ++first)
    {
        auto iter = std::min_element(first, last, comp);
        std::swap(*first, *iter);
    }
}

and the simpler version

template <typename ForwardIter, 
    typename Comp = std::less<>>
void select_sort(ForwardIter first, ForwardIter last, Comp comp = Comp())
{
    // Same as above
}

Both seem to work. Is it just a style issue? Or are the cases where one have to choose one or the other?

2 Answers 2

2

Both will work but they are different.

std::less<T> is the most commonly use case. It's a class that have the operator() overloaded. It is implemented in a similar way as this.

I omitted some special cases about pointers here, but it's only a naive implementation for the sake of simplicity.

template<typename T>
struct less {
    constexpr bool operator()(const T &lhs, const T &rhs) const {
        return lhs < rhs;
    }
};

It's like a functor, but you choose which T it will be. The advantage of this solution is that one can specialize std::less<T> for special cases of one's code.

std::less<> is different. It can compare object of any types, as long as the two types has operator< overloaded. It act just like a generic lambda. It is implemented a bit like this:

template<>
struct less<void> {
    template<typename T, typename U>
    constexpr auto operator()(T&& lhs, U&& rhs) const
            -> decltype(std::declval<T>() < std::declval<U>()) {
        return std::forward<T>(lhs) < std::forward<U>(rhs);
    }
};

As you can see, when you use std::less<>{}(a, b), it's really close to the same thing as if you wrote a < b, and will work even with types that has a non-const operator<. So this one is the best, as long as the classes you use have the operator< overloaded.

Sign up to request clarification or add additional context in comments.

1 Comment

For completeness: the special exception that std::less<T *>() is even allowed for pointers that don't point into the same array (unlike <) also extends to std::less<>(). The definition you put in this answer wouldn't handle that properly. (I do realise you wrote "a bit like".)
2

typename Comp = std::less<typename std::iterator_traits<ForwardIter>::value_type> means that when Comp is not specified, std::less<typename std::iterator_traits<ForwardIter>::value_type> is used.

typename Comp = std::less<> means that when Comp is not specified, std::less<> is used.

That's the exact difference. It doesn't mean that the compiler somehow deduces the template argument for std::less<>. Instead, since the template std::less<T=void> specifies a default value, that default value is used.

The template specialisation std::less<void> is defined in such a way that it can generally be used in place of other std::less<T> versions. They're not exactly the same thing, there are cases where you'd use one or the other. From your question title though, your question appears to be about the syntax of the default argument, not about std::less specifically, so I'll skip the explanation of that.

3 Comments

As far as I can tell, the first snippet is not valid until C++14
@EdgarRokyan Which is the current version of C++, so assumed unless otherwise specified. (I think you mean "second snippet", by the way.)
Yes, definitely. Seems that I lost a concentration to the end of the day :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.