3153. Common and common_type have too little in common

Section: 18.4.6 [concept.common] Status: C++20 Submitter: Casey Carter Opened: 2018-08-10 Last modified: 2021-02-25

Priority: 0

View all other issues in [concept.common].

View all issues with C++20 status.

Discussion:

The Common concept when applied to types T and U requires that T and U are each ConvertibleTo ( [concept.convertibleto]) their common type common_type_t<T, U>. ConvertibleTo requires both implicit and explicit conversions with equivalent results. The requirement for implicit conversion is notably not a requirement for specializing common_type as detailed in 21.3.8.7 [meta.trans.other]:

-5- Such a specialization need not have a member named type, but if it does, that member shall be a typedef-name for an accessible and unambiguous cv-unqualified non-reference type C to which each of the types T1 and T2 is explicitly convertible.

which only requires explicit conversion to be valid. While it's not inconsistent that the Common concept's requirements are a refinement of the requirements for common_type, there's no good reason for this additional requirement. The stated design intent is to enable writing monomorphic predicates that can compare Ts with Us (and vice versa) by accepting two arguments of type common_type_t<T, U>, but this role has been superseded by the addition of CommonReference and common_reference_t to the ranges design. The existence of pairs of types that are only explicitly convertible to their common type suggests that using Common in this way would never be a fully generic solution in any case.

The only existing use of the Common concept in either the working draft or the Ranges proposal is as a soundness check on the comparison and difference operators of counted_iterator, none of which actually convert any argument to the common type in their normal operation. It would seem that we could strike the additional requirement without impacting the Ranges design, which would allow for future uses of the Common concept with types like chrono::duration (30.5 [time.duration]) which sometimes provide only explicit conversion to a common type.

Notably, removing the requirement for implicit conversion will also make the Common concept consistent with the description in 18.4.6 [concept.common] p1: "If T and U can both be explicitly converted to some third type, C, then T and U share a common type, C."

[2018-08 Batavia Monday issue prioritization]

P0; Status to 'Tentatively Ready' after adding two semicolons to the P/R.

[2018-11, Adopted in San Diego]

Proposed resolution:

This wording is relative to N4762.

  1. Modify the definition of Common in 18.4.6 [concept.common] as follows:

    template<class T, class U>
      concept Common =
        Same<common_type_t<T, U>, common_type_t<U, T>> &&
        ConvertibleTo<T, common_type_t<T, U>> &&
        ConvertibleTo<U, common_type_t<T, U>> &&
        requires {
          static_cast<common_type_t<T, U>>(declval<T>());
          static_cast<common_type_t<T, U>>(declval<U>());
        } &&
        CommonReference<
          add_lvalue_reference_t<const T>,
          add_lvalue_reference_t<const U>> &&
        CommonReference<
          add_lvalue_reference_t<common_type_t<T, U>>,
          common_reference_t<
            add_lvalue_reference_t<const T>,
            add_lvalue_reference_t<const U>>>;