1. Revision History
1.1. Revision 0
Initial release.
2. Motivation
Introducing
is mainly a matter of convenience: for most purposes
is enough to not take a branch when
evaluates to true
. The main idea is to lift the negation into the boolean domain since that is the domain conditional statements typically deal with.
Moreover there are a few corner cases where it is not possible to negate the
condition naively without a workaround, which this proposal intends to make easier to deal with. Consider the examples in the following subsections.
2.1. Declaring a variable with if
When
is used to declare a variable, there was no easy and clean way to negate the condition before C++17:
// C++14, best effort, might be confusing if ( auto opt = some_optional ()); else { // Do stuff } // C++17, separate declaration and condition if ( auto opt = some_optional () ; not opt ) { // Do stuff }
With the proposed extensions, the condition above could be written as follows:
if not ( auto opt = some_optional ()) { // Do stuff }
We believe that the notation itself is clear enough and should be rather obvious to everyone reading the conditional statement.
2.2. Avoiding tricky overloads of operator !
If
is overloaded,
is not guaranteed to have a result contextually convertible to
, which might result in tricky corner cases when writing generic algorithms. Consider the following naive implementation of
:
template < typename InputIterator , typename Predicate > InputIterator find_if_not ( InputIterator first , InputIterator last , Predicate pred ) { for (; first != last ; ++ first ) { // Fails to compile when operator! doesn’t return a // result contextually convertible to bool if ( ! pred ( * first )) { break ; } } return first ; }
If
is overloaded in such a way that it doesn’t return a value contextually convertibe to
, the algorithm above will fail to compile, despite
not violating the Predicate requirements (the issue came up while reviewing the inclusion of the Ranges TS in C++20, see also [LWG2114]). We propose that
applies the negation after the contextual conversion to
, avoiding this subtle class of problems altogether:
template < typename InputIterator , typename Predicate > InputIterator find_if_not ( InputIterator first , InputIterator last , Predicate pred ) { for (; first != last ; ++ first ) { // Safe even when operator! is overloaded: the contextual // conversion to bool is applied before the negation if not ( pred ( * first )) { break ; } } return first ; }
The current workaround is to use
before
is called which, as far as I know, is not what standard library implementations of algorithms do. Using
would be an idiomatic solution to avoid this kind of problem and produce more correct generic code without extra effort. It would ensure that the negation always happens in the boolean domain.
3. Clarifications & Extensions
First of all, while I use
through the proposal because of personal preference, it is intended that
works too. Second,
should also negate the condition even in the presence of an init-statement as introduced in C++17.
3.1. while not
The control flow statements
and
tend to evolve in a somewhat symmetrical manner, we propose to allow
and
to benefit from the same augmentation in a similar manner.
3.2. if constexpr
It would be logical to allow the same extension
to benefit from the same extension. The form
would be preferred to
because it subjectively reads better when spelled with an exclamation mark. On the other hand [P1073] introduces a
token which would probably be preferred by max munch rule unless special cased if one tried to write
, which might be seen as a drawback. This will probably need some bikeshed.