2040. Missing type traits related to is_convertible

Section: 23.15 [meta] Status: LEWG Submitter: Daniel Krügler Opened: 2011-03-03 Last modified: 2017-07-16

Priority: Not Prioritized

View other active issues in [meta].

View all other issues in [meta].

View all issues with LEWG status.

Discussion:

When n3142 was suggested, it concentrated on constructions, assignments, and destructions, but overlooked to complement the single remaining compiler-support trait

template <class From, class To> struct is_convertible;

with the no-throw and triviality related aspects as it had been done with the other expression-based traits. Specifically, the current specification misses to add the following traits:

template <class From, class To> struct is_nothrow_convertible;
template <class From, class To> struct is_trivially_convertible;

In particular the lack of is_nothrow_convertible is severly restricting. This was recently recognized when the proposal for decay_copy was prepared by n3255. There does not exist a portable means to define the correct conditional noexcept specification for the decay_copy function template, which is declared as:

template <class T> 
typename decay<T>::type decay_copy(T&& v) noexcept(???);

The semantics of decay_copy bases on an implicit conversion which again influences the overload set of functions that are viable here. In most circumstances this will have the same effect as comparing against the trait std::is_nothrow_move_constructible, but there is no guarantee for that being the right answer. It is possible to construct examples, where this would lead to the false result, e.g.

struct S {
  S(const S&) noexcept(false);
 
  template<class T>
  explicit S(T&&) noexcept(true);
};

std::is_nothrow_move_constructible will properly honor the explicit template constructor because of the direct-initialization context which is part of the std::is_constructible definition and will in this case select it, such that std::is_nothrow_move_constructible<S>::value == true, but if we had the traits is_nothrow_convertible, is_nothrow_convertible<S, S>::value would evaluate to false, because it would use the copy-initialization context that is part of the is_convertible definition, excluding any explicit constructors and giving the opposite result.

The decay_copy example is surely not one of the most convincing examples, but is_nothrow_convertible has several use-cases, and can e.g. be used to express whether calling the following implicit conversion function could throw an exception or not:

template<class T, class U>
T implicit_cast(U&& u) noexcept(is_nothrow_convertible<U, T>::value) 
{
  return std::forward<U>(u);
}

Therefore I suggest to add the missing trait is_nothrow_convertible and for completeness also the missing trait is_trivially_convertible to 23.15 [meta].

[2011-03-24 Madrid meeting]

Daniel K: This is a new feature so out of scope.

Pablo: Any objections to moving 2040 to Open?

No objections.

[Bloomington, 2011]

Move to NAD Future, this would be an extension to existing functionality.

Proposed resolution:

  1. Ammend the following declarations to the header <type_traits> synopsis in 23.15.2 [meta.type.synop]:

    namespace std {
      …
      // 20.9.6, type relations:
      template <class T, class U> struct is_same;
      template <class Base, class Derived> struct is_base_of;
      template <class From, class To> struct is_convertible;
      template <class From, class To> struct is_trivially_convertible;
      template <class From, class To> struct is_nothrow_convertible;
    
      …
    }
    
  2. Modify Table 51 — "Type relationship predicates" as indicated. The removal of the remaining traces of the trait is_explicitly_convertible is an editorial step, it was removed by n3047:

    Table 51 — Type relationship predicates
    Template Condition Comments
    template <class From, class To>
    struct is_convertible;
    see below From and To shall be complete
    types, arrays of unknown bound, or
    (possibly cv-qualified) void
    types.
    template <class From, class To>
    struct is_explicitly_convertible;
    is_constructible<To, From>::value a synonym for a two-argument
    version of is_constructible.
    An implementation may define it
    as an alias template.
    template <class From, class To>
    struct is_trivially_convertible;
    is_convertible<From,
    To>::value
    is true and the
    conversion, as defined by
    is_convertible, is known
    to call no operation that is
    not trivial ([basic.types], [special]).
    From and To shall be complete
    types, arrays of unknown bound,
    or (possibly cv-qualified) void
    types.
    template <class From, class To>
    struct is_nothrow_convertible;
    is_convertible<From,
    To>::value
    is true and the
    conversion, as defined by
    is_convertible, is known
    not to throw any
    exceptions ([expr.unary.noexcept]).
    From and To shall be complete
    types, arrays of unknown bound,
    or (possibly cv-qualified) void
    types.