Header file range/v3/utility/common_type.hpp

#include <meta/meta.hpp>

#include <range/v3/range_fwd.hpp>

namespace std{}

namespace ranges
{
    inline namespace v3
    {
        namespace detail
        {
            template <typename From, typename To>
            struct _copy_cv_
            {
                using type = To;
            };
            
            template <typename From, typename To>
            struct _copy_cv_<From const, To>
            {
                using type = To const;
            };
            
            template <typename From, typename To>
            struct _copy_cv_<From volatile, To>
            {
                using type = To volatile;
            };
            
            template <typename From, typename To>
            struct _copy_cv_<From const volatile, To>
            {
                using type = To const volatile;
            };
            
            template <typename From, typename To>
            using _copy_cv = meta::_t<_copy_cv_<From, To>>;
            
            template <typename T, typename U>
            using _builtin_common_t = meta::_t<_builtin_common<T, U>>;
            
            template <typename T, typename U>
            using _cond_res = decltype(true?std::declval<T>():std::declval<U>());
            
            template <typename T, typename U, typename R = _builtin_common_t<T&, U&>>
            using _rref_res = meta::if_<std::is_reference<R>, meta::_t<std::remove_reference<R>>&&, R>;
            
            template <typename T, typename U>
            using _lref_res = _cond_res<_copy_cv<T, U>&, _copy_cv<U, T>&>;
            
            template <typename T, typename U, typename>
            struct _builtin_common
            : meta::lazy::let<meta::defer<decay_t, meta::defer<_cond_res, as_cref_t<T>, as_cref_t<U> > > >
            {};
            
            template <typename T, typename U>
            struct _builtin_common<T&&, U&&, meta::if_<meta::and_<std::is_convertible<T&&, _rref_res<T, U>>, std::is_convertible<U&&, _rref_res<T, U>>>>>
            {
                using type = _rref_res<T, U>;
            };
            
            template <typename T, typename U>
            struct _builtin_common<T&, U&>
            : meta::defer<_lref_res, T, U>
            {};
            
            template <typename T, typename U>
            struct _builtin_common<T&, U&&, meta::if_<std::is_convertible<U&&, _builtin_common_t<T&, U const&>>>>
            : _builtin_common<T &, const U &>
            {};
            
            template <typename T, typename U>
            struct _builtin_common<T&&, U&>
            : _builtin_common<U &, T &&>
            {};
        }
        
        template <typename ... Ts>
        struct common_type;
        
        template <typename T>
        struct common_type<T>
        : std::decay<T>
        {};
        
        template <typename T, typename U>
        struct common_type<T, U>
        : meta::if_c<(std::is_same<detail::decay_t<T>, T>::value && std::is_same<detail::decay_t<U>, U>::value), meta::defer<detail::_builtin_common_t, T, U>, common_type<detail::decay_t<T>, detail::decay_t<U> > >
        {};
        
        template <typename T, typename U, typename ... Vs>
        struct common_type<T, U, Vs...>
        : meta::lazy::fold<meta::list<U, Vs...>, T, meta::quote<common_type_t> >
        {};
        
        template <typename T, typename U, typename TQual, typename UQual>
        struct basic_common_reference;
        
        namespace detail
        {
            using _rref = meta::quote_trait<std::add_rvalue_reference>;
            
            using _lref = meta::quote_trait<std::add_lvalue_reference>;
            
            template <typename T>
            struct _xref
            {
                using type = meta::quote_trait<meta::id>;
            };
            
            template <typename T>
            struct _xref<T&&>
            {
                using type = meta::compose<_rref, meta::_t<_xref<T>>>;
            };
            
            template <typename T>
            struct _xref<T&>
            {
                using type = meta::compose<_lref, meta::_t<_xref<T>>>;
            };
            
            template <typename T>
            struct _xref<T const>
            {
                using type = meta::quote_trait<std::add_const>;
            };
            
            template <typename T>
            struct _xref<T volatile>
            {
                using type = meta::quote_trait<std::add_volatile>;
            };
            
            template <typename T>
            struct _xref<T const volatile>
            {
                using type = meta::quote_trait<std::add_cv>;
            };
            
            template <typename T, typename U>
            using _basic_common_reference = basic_common_reference<uncvref_t<T>, uncvref_t<U>, meta::_t<_xref<T>>, meta::_t<_xref<U>>>;
            
            template <typename T, typename U, typename = void>
            struct _common_reference2
            : meta::if_<meta::is_trait<_basic_common_reference<T, U> >, _basic_common_reference<T, U>, common_type<T, U> >
            {};
            
            template <typename T, typename U>
            struct _common_reference2<T, U, meta::if_<std::is_reference<_builtin_common_t<T, U>>>>
            : _builtin_common<T, U>
            {};
        }
        
        template <typename ... Ts>
        struct common_reference;
        
        template <typename T>
        struct common_reference<T>
        {
            using type = T;
        };
        
        template <typename T, typename U>
        struct common_reference<T, U>
        : detail::_common_reference2<T, U>
        {};
        
        template <typename T, typename U, typename ... Vs>
        struct common_reference<T, U, Vs...>
        : meta::lazy::fold<meta::list<U, Vs...>, T, meta::quote<common_reference_t> >
        {};
    }
}

Class template ranges::v3::common_type<Ts...>

template <typename ... Ts>
struct common_type{};

Users should specialize this to hook the \c Common concept until \c std gets a SFINAE-friendly \c std::common_type and there's some sane way to deal with cv and ref qualifiers.


Class template ranges::v3::basic_common_reference<T, U, TQual, UQual>

template <typename T, typename U, typename TQual, typename UQual>
struct basic_common_reference{};

Users can specialize this to hook the \c CommonReference concept. common_reference


Class template ranges::v3::common_reference<Ts...>

template <typename ... Ts>
struct common_reference{};

Users can specialize this to hook the \c CommonReference concept. basic_common_reference