2

The std::set has the below template parameters:

template<typename _Key, typename _Compare = std::less<_Key>,
         typename _Alloc = std::allocator<_Key> >
class set

And std::greater has the below template parameter:

template<typename _Tp>
struct greater : public binary_function<_Tp, _Tp, bool>

The below declarations can both be compiled on gcc 7.1. I don't understand why the second declaration is valid. Is it a C++14 feature? Is the rule defined somewhere in the standard?

std::set<int, std::greater<int>> s1;
std::set<int, std::greater<>> s2;
alex
  • 193
  • 2
  • 8
  • It *is* a C++14 feature. The specification speaks lots: http://en.cppreference.com/w/cpp/utility/functional/greater – DeiDei Apr 10 '18 at 05:50

3 Answers3

3

Is it a C++14 feature?

Yes, it is:

template< class T >
struct greater; // Until C++14

template< class T = void >
struct greater; // Since C++14

Without type T specified, it gets the default one, void.

Dean Seo
  • 5,486
  • 3
  • 30
  • 49
3

It is a C++14 standard library feature called transparent comparators.

The below declarations can both be compiled

std::set<int, std::greater<int>> s1;
std::set<int, std::greater<>> s2;

The second variant allows for a heterogeneous comparison. Not a big deal in the case of int but consider the example from N3657:

std::set<std::string> s = /* ... */;
s.find("key");

In old C++11 this will construct a string temporary in order to invoke less(string, string).

With a transparent comparator in C++14, less::operator() (const string&, const char*)1 will be generated automatically which will invoke bool operator< (const string&, const char*), thus avoiding the temporary.

The way it's implemented is:

template< class T = void >
struct greater;

template <> struct greater<void> {
  template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) > std::forward<U>(u));
};

That is, when no T is specified, it defaults to void, which in C++14 has a specialization that accepts heterogeneous parameters. The assumption here is that no-one would use std::greater<void> in older code, so the risk of existing code breaking is small.

For more details see the proposals N3421, which introduces the "diamond operators", and N3657, which introduces std::is_transparent.


1 Disclaimer: the exact deduced types may be different, this is just to illustrate the core concept without getting into too much detail.

rustyx
  • 80,671
  • 25
  • 200
  • 267
1

Is the rule defined somewhere in the standard?

From comparisons.greater:

template<class T = void> struct greater {
  constexpr bool operator()(const T& x, const T& y) const;
};
Joseph D.
  • 11,804
  • 3
  • 34
  • 67