简体   繁体   中英

Why std::compare_three_way is not a template struct/functor

For example, the compare of std::less is defined as a template struct

template< class T = void >
struct less;

While std::compare_three_way is defined just as a normal struct, and its operator() is a template function. (code from MSVC)

struct compare_three_way {
    template <class _Ty1, class _Ty2>
        requires three_way_comparable_with<_Ty1, _Ty2> // TRANSITION, GH-489
    constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
        noexcept(noexcept(_STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right))) /* strengthened */ {
        return _STD forward<_Ty1>(_Left) <=> _STD forward<_Ty2>(_Right);
    }

    using is_transparent = int;
};

So why isn't std::compare_three_way a template struct?

template <class _Ty1, class _Ty2>
    requires three_way_comparable_with<_Ty1, _Ty2>
struct compare_three_way {
    constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const; 
};

By the way, could I replace std::less<T> by std::three_way_compare in my own implementation of containers, like Comparer<T> in C#.

The comparison of original std::less (and its friends) are defined as such:

bool operator()( const T& lhs, const T& rhs ) const;

As this function call operator is not a template, it can only compare objects of the type that is used to instantiate the std::less template.

In a later language revision, these comparators were extended with specialisation std::less<> to support comparing objects of different types by templating the the function call operator itself:

template< class T, class U>
constexpr auto operator()( T&& lhs, U&& rhs ) const
  -> decltype(std::forward<T>(lhs) < std::forward<U>(rhs));
    

This largely obsoletes the homogeneous versions ( std::less<T> ) in most use cases because it is either equivalent, or more efficient due to not forcing a conversion to a common type. The old homogeneous comparators are retained for backward compatibility.

Heterogeneous lookup was an existing thing when std::compare_three_way was proposed, so the homogeneous version was never introduced.

To use it in template, like std::less<T> in std::set<T>

You can use std::compare_three_way in std::set<T> and std::set<U> etc. (figuratively) just like you can use std::less<> . You (probably) don't need std::less<T> nor std::less<U> - nor std::compare_three_way<T> which doesn't exist.

As it looks like the std::compare_three_way performs forwarding, it could not work when the template parameters are for a struct . C++ Standard section 13.10.3.2 §3 :

A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])).

Thus, here _Ty1&& and _Ty2&& are just rvalue references, and there is no way to forward them correctly:

template <class _Ty1, class _Ty2>
    requires three_way_comparable_with<_Ty1, _Ty2>
struct compare_three_way {
    constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const; 
};

The template struct declared above only accepts rvalue expressions as its operands, which would not be too useful. The only way to have this struct be usable is to make the arguments const references. The MSVC implementers have opted for the forwarding way instead.

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