This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD Concepts status.

### 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: