1076. unary/binary_negate need constraining and move support

Section: 99 [depr.negators] Status: NAD Concepts Submitter: Alisdair Meredith Opened: 2009-03-20 Last modified: 2017-06-15

Priority: Not Prioritized

View all issues with NAD Concepts status.

Discussion:

The class templates unary/binary_negate need constraining and move support.

Ideally these classes would be deprecated, allowing unary/binary_function to also be deprecated. However, until a generic negate adaptor is introduced that can negate any Callable type, they must be supported so should be constrained. Likewise, they should be movable, and support adopting a move-only predicate type.

In order to preserve ABI compatibility, new rvalue overloads are supplied in preference to changing the existing pass-by-const-ref to pass-by-value.

Do not consider the issue of forwarding mutable lvalues at this point, although remain open to another issue on the topic.

[ 2009-05-01 Daniel adds: ]

IMO the currently proposed resolution needs some updates because it is ill-formed at several places:

  1. In concept AdaptableUnaryFunction change

    typename X::result_type;
    typename X::argument_type;
    

    to

    Returnable result_type = typename X::result_type;
    typename argument_type = typename X::argument_type;
    

    [The replacement "Returnable result_type" instead of "typename result_type" is non-editorial, but maybe you prefer that as well]

  2. In concept AdaptableBinaryFunction change

    typename X::result_type;
    typename X::first_argument_type;
    typename X::second_argument_type;
    

    to

    Returnable result_type = typename X::result_type;
    typename first_argument_type = typename X::first_argument_type;
    typename second_argument_type = typename X::second_argument_type;
    

    [The replacement "Returnable result_type" instead of "typename result_type" is non-editorial, but maybe you prefer that as well.]

  3. In class unary/binary_function

    1. I suggest to change "ReturnType" to "Returnable" in both cases.
    2. I think you want to replace the remaining occurrences of "Predicate" by "P" (in both classes in copy/move from a predicate)
  4. I think you need to change the proposed signatures of not1 and not2, because they would still remain unconstrained: To make them constrained at least a single requirement needs to be added to enable requirement implication. This could be done via a dummy ("requires True<true>") or just explicit as follows:

    1. template <AdaptableUnaryFunction P>
      requires Predicate< P, P::argument_type>
      unary_negate<P> not1(const P&& pred);
      template <AdaptableUnaryFunction P>
      requires Predicate< P, P::argument_type >
      unary_negate<P> not1(P&& pred);
      

      -3- Returns: unary_negate<P>(pred).

      [Don't we want a move call for the second overload as in

      unary_negate<P>(std::move(pred))
      

      in the Returns clause ?]

    2. template <AdaptableBinaryFunction P>
      requires Predicate< P, P::first_argument_type, P::second_argument_type >
      binary_negate<P> not2(const P& pred);
      template <AdaptableBinaryFunction P>
      requires Predicate< P, P::first_argument_type, P::second_argument_type >
      binary_negate<P> not2(P&& pred);
      

      -5- Returns: binary_negate<P>(pred).

      [Don't we want a move call for the second overload as in

      binary_negate<P>(std::move(pred))
      

      in the Returns clause ?]

[ Batavia (2009-05): ]

There is concern that complicating the solution to preserve the ABI seems unnecessary, since we're not in general preserving the ABI.

We would prefer a separate paper consolidating all Clause 20 issues that are for the purpose of providing constrained versions of the existing facilities.

Move to Open.

[ 2009-10 post-Santa Cruz: ]

Leave open pending the potential move constructor paper. Note that we consider the "constraining" part NAD Concepts.

[ 2010-01-31 Alisdair removes the current proposed wording from the proposed wording section because it is based on concepts. That wording is proposed here: ]

Add new concepts where appropriate::

auto concept AdaptableUnaryFunction< typename X > {
  typename X::result_type;
  typename X::argument_type;
}

auto concept AdaptableBinaryFunction< typename X > {
  typename X::result_type;
  typename X::first_argument_type;
  typename X::second_argument_type;
}

Revise as follows:

Base [base] (Only change is constrained Result)

-1- The following classes are provided to simplify the typedefs of the argument and result types:

namespace std {
  template <class Arg, class ReturnType Result>
  struct unary_function {
     typedef Arg    argument_type;
     typedef Result result_type;
  };

  template <class Arg1, class Arg2, class ReturnType Result>
  struct binary_function {
     typedef Arg1   first_argument_type;
     typedef Arg2   second_argument_type;
     typedef Result result_type;
  };
}

Negators [negators]:

-1- Negators not1 and not2 take a unary and a binary predicate, respectively, and return their complements (5.3.1).

template <class AdaptableUnaryFunction Predicate>
  requires Predicate< P, P::argument_type >
  class unary_negate
    : public unary_function<typename Predicate::argument_type,bool> {
  public:
    unary_negate(const unary_negate & ) = default;
    unary_negate(unary_negate && );

    requires CopyConstructible< P >
       explicit unary_negate(const Predicate& pred); 
    requires MoveConstructible< P >
       explicit unary_negate(Predicate && pred);

    bool operator()(const typename Predicate::argument_type& x) const;
  };

-2 operator() returns !pred(x).

template <class Predicate>
  unary_negate<Predicate> not1(const Predicate&amp; pred);
template <class Predicate>
  unary_negate<Predicate> not1(Predicate&& pred);

-3- Returns: unary_negate<Predicate>(pred).

template <class AdaptableBinaryFunction Predicate >
  requires Predicate< P, P::first_argument_type, P::second_argument_type >
  class binary_negate
    : public binary_function<typename Predicate::first_argument_type,
                              typename Predicate::second_argument_type, bool> {
  public:
    biary_negate(const binary_negate & ) = default;
    binary_negate(binary_negate && );

    requires CopyConstructible< P >
       explicit binary_negate(const Predicate& pred);
    requires MoveConstructible< P >
       explicit binary_negate(const Predicate& pred);

    bool operator()(const typename Predicate::first_argument_type& x,
                    const typename Predicate::second_argument_type& y) const;
  };

-4- operator() returns !pred(x,y).

template <class Predicate>
  binary_negate<Predicate> not2(const Predicate& pred);
template <class Predicate>
  binary_negate<Predicate> not2(Predicate&& pred);

-5- Returns: binary_negate<Predicate>(pred).

[ 2010 Rapperswil: ]

Move to NAD Concepts. The move-semantic part has been addressed by a core language change, which implicitly generates appropriate move constructors and move-assignment operators.

Proposed resolution: