3680. Constructor of move_only_function with empty ref-qualifier is over-constrained

Section: 22.10.17.4.3 [func.wrap.move.ctor] Status: New Submitter: Zhihao Yuan Opened: 2022-02-27 Last modified: 2022-03-04

Priority: 2

View other active issues in [func.wrap.move.ctor].

View all other issues in [func.wrap.move.ctor].

View all issues with New status.

Discussion:

The following test compiles and holds:

struct X
{
  int operator()() & { return 1; }  // #1
  int operator()() && { return 2; } // #2
};

using T = move_only_function<int()>;
assert(T(X{})() == 1);

In other words, #2 is never used. But if you really remove #2, the code doesn't compile.

The change was introduced between P0288R5 and P0288R6, with an intention (assumed) to require move_only_function<R(Args...) cv> erasing callable objects with operator() without ref-qualifiers. But the actual outcome outlawed programs that were valid when using std::function.

It also outlaws future programs such as

T x = [captures](this auto&) { return ...; }

where declaring this auto&& without forwarding only to satisfy move_only_function's constraints would be strange.

[2022-03-04; Reflector poll]

Set priority to 2 after reflector poll. Probably needs a paper for LEWG.

Proposed resolution:

This wording is relative to N4901.

  1. Modify 22.10.17.4.3 [func.wrap.move.ctor] as indicated:

    template<class VT>
      static constexpr bool is-callable-from = see below;
    

    -1- If noex is true, is-callable-from<VT> is equal to:

    is_nothrow_invocable_r_v<R, VT cv ref, ArgTypes...> &&
    is_nothrow_invocable_r_v<R, VT inv-quals, ArgTypes...>
    

    Otherwise, is-callable-from<VT> is equal to:

    is_invocable_r_v<R, VT cv ref, ArgTypes...> &&
    is_invocable_r_v<R, VT inv-quals, ArgTypes...>