2957. bind's specification doesn't apply the cv-qualification of the call wrapper to the callable object

Section: 22.10.15.4 [func.bind.bind] Status: Resolved Submitter: Tim Song Opened: 2017-05-04 Last modified: 2020-09-06

Priority: 3

View all other issues in [func.bind.bind].

View all issues with Resolved status.

Discussion:

According to 22.10.15.4 [func.bind.bind]/1.2,

fd is an lvalue of type FD constructed from std::forward<F>(f),

and then uses fd throughout the specification, seemingly without regard to the cv-qualification of the call wrapper g. But this definition means that fd is always cv-unqualified, rather than having its cv-qualification change with that of g as intended.

LWG 2545 accidentally exacerbated the problem by removing any hint that fd's cv-qualifier followed that of the call wrapper.

A similar issue affects the type of tdi for nested binds.

[2017-07 Toronto Wed Issue Prioritization]

Priority 3

[2020-01 Resolved by the adoption of P1065 in Cologne.]

Proposed resolution:

This wording is relative to N4659.

  1. Edit 22.10.15.4 [func.bind.bind] as indicated:

    template<class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    […]

    -3- Returns: A forwarding call wrapper g (22.10.4 [func.require]). The effect of g(u1, u2, ..., uM) shall be

    INVOKE(static_cast<FD cv &>(fd), std::forward<V1>(v1), 
      std::forward<V2>(v2), ..., std::forward<VN>(vN))
    

    where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, . . . , vN are determined as specified below. The copy constructor and move constructor of the forwarding call wrapper shall throw an exception if and only if the corresponding constructor of FD or of any of the types TDi throws an exception.

    […]

    template<class R, class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    […]

    -7- Returns: A forwarding call wrapper g (22.10.4 [func.require]). The effect of g(u1, u2, ..., uM) shall be

    INVOKE<R>(static_cast<FD cv &>(fd), std::forward<V1>(v1), 
      std::forward<V2>(v2), ..., std::forward<VN>(vN))
    

    where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, . . . , vN are determined as specified below. The copy constructor and move constructor of the forwarding call wrapper shall throw an exception if and only if the corresponding constructor of FD or of any of the types TDi throws an exception.

    […]

    -10- The values of the bound arguments v1, v2, ... , vN and their corresponding types V1, V2, ... , VN depend on the types TDi derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:

    1. (10.1) — […]

    2. (10.2) — if the value of is_bind_expression_v<TDi> is true, the argument is static_cast<TDi cv &>(tdi)(std::forward<Uj>(uj)...) and its type Vi is invoke_result_t<TDi cv &, Uj...>&&;

    3. (10.3) — […]