Title
Passing function types to result_of and is_callable
Status
resolved
Section
[meta.rel]
Submitter
Great Britain

Created on 2017-02-03.00:00:00 last changed 93 months ago

Messages

Date: 2017-03-15.00:00:00

[ 2017-03-12, post-Kona ]

Resolved by p0604r0.

Date: 2017-02-15.00:00:00

[ 2017-02-24, Daniel comments ]

I suggest to apply the paper d0604r0 instead, available on the Kona LWG wiki.

Date: 2017-02-15.00:00:00

[ 2017-02-22, Daniel comments and provides concrete wording ]

The approach chosen to resolve this issue is a merger with LWG 2928, that is the callable traits are also renamed to invocable.

Previous resolution [SUPERSEDED]:

This wording is relative to N4640.

  1. Modify [meta.type.synop], header <type_traits> synopsis, as indicated:

    […]
    // 20.15.6, type relations
    […]
    
    template <class, class R = void> struct is_callable; // not defined
    template <class Fn, class... ArgTypes, class R>
    struct is_callable<Fn(ArgTypes...), R>;
    template <class Fn, class... ArgTypes> struct is_invocable;
    template <class R, class Fn, class... ArgTypes> struct is_invocable_r;
    
    template <class, class R = void> struct is_nothrow_callable; // not defined
    template <class Fn, class... ArgTypes, class R>
    struct is_nothrow_callable<Fn(ArgTypes...), R>;
    template <class Fn, class... ArgTypes> struct is_nothrow_invocable;
    template <class R, class Fn, class... ArgTypes> struct is_nothrow_invocable_r;
    
    […]
    
    // 20.15.6, type relations
    […]
    template <class T, class R = void> constexpr bool is_callable_v
    = is_callable<T, R>::value;
    template <class T, class R = void> constexpr bool is_nothrow_callable_v
    = is_nothrow_callable<T, R>::value;
    template <class Fn, class... ArgTypes> constexpr bool is_invocable_v
    = is_invocable<Fn, ArgTypes...>::value;
    template <class R, class Fn, class... ArgTypes> constexpr bool is_invocable_r_v
    = is_invocable_r<R, Fn, ArgTypes...>::value;
    template <class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_v
    = is_nothrow_invocable<Fn, ArgTypes...>::value;
    template <class R, class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_r_v
    = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;
    […]
    
  2. Modify [meta.rel], Table 44 — "Type relationship predicates", as indicated:

    Table 44 — Type relationship predicates
    […]
    template <class Fn, class...
    ArgTypes, class R>
    struct is_invocablecallable<
    Fn(ArgTypes...), R>
    ;
    The expression
    INVOKE(declval<Fn>(),
    declval<ArgTypes>()...,
    R
    )
    is well formed when treated
    as an unevaluated operand
    Fn, R, and all types in the
    parameter pack ArgTypes shall
    be complete types, cv void, or
    arrays of unknown bound.
    template <class R, class Fn, class...
    ArgTypes>
    struct is_invocable_r;
    The expression
    INVOKE(declval<Fn>(),
    declval<ArgTypes>()...,
    R)
    is well formed when treated
    as an unevaluated operand
    Fn, R, and all types in the
    parameter pack ArgTypes shall
    be complete types, cv void, or
    arrays of unknown bound.
    template <class Fn, class...
    ArgTypes, class R>
    struct is_nothrow_invocablecallable<
    Fn(ArgTypes...), R>
    ;
    is_invocablecallable_v<
    Fn, ArgTypes...Fn(ArgTypes...), R>
    is
    true and the expression
    INVOKE(declval<Fn>(),
    declval<ArgTypes>()...,
    R
    )
    is known not to throw any
    exceptions
    Fn, R, and all types in the
    parameter pack ArgTypes shall
    be complete types, cv void, or
    arrays of unknown bound.
    template <class R, class Fn, class...
    ArgTypes, class R>
    struct is_nothrow_invocable_r;
    is_invocable_r_v<
    R, Fn, ArgTypes...>
    is
    true and the expression
    INVOKE(declval<Fn>(),
    declval<ArgTypes>()...,
    R)
    is known not to throw any
    exceptions
    Fn, R, and all types in the
    parameter pack ArgTypes shall
    be complete types, cv void, or
    arrays of unknown bound.
Date: 2017-02-05.13:39:22

[ 2017-02, pre-Kona ]

See also LWG 2927.

Date: 2017-02-05.13:39:22
Addresses GB 55

It is becoming more and more apparent that using a function type as the template argument to result_of causes annoying problems. That was done because C++03 didn't have variadic templates, so it allowed an arbitrary number of types to be smuggled into the template via a single parameter, but it's a hack and unnecessary in C++ today. result_of<F(Args...)> has absolutely nothing to do with a function type that returns F, and the syntactic trickery using a function type has unfortunate consequences such as top-level cv-qualifiers and arrays decaying (because those are the rules for function types).

It might be too late to change result_of, but we should not repeat the same mistake for std::is_callable.

Proposed change: Possibly get rid of the is_callable<Fn(ArgTypes?...), R> specialization. Change the primary template is_callable<class, class R = void> to is_callable<class Fn, class.. ArgTypes?> and define a separate template such as is_callable_r<class R, class Fn, class... ArgTypes?> for the version that checks the return type. The resulting inconsistency might need to be resolved/improved upon.

History
Date User Action Args
2017-03-12 23:04:12adminsetstatus: new -> resolved
2017-02-24 21:23:01adminsetmessages: + msg8995
2017-02-22 20:56:04adminsetmessages: + msg8982
2017-02-22 20:56:04adminsetmessages: + msg8981
2017-02-05 13:39:22adminsetmessages: + msg8932
2017-02-03 00:00:00admincreate