This is a proposal to add a new form of condition:
condition:
expression
attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list
attribute-specifier-seqopt decl-specifier-seq declarator : expression
For example, if (T x : e) s translates to if (auto && __p = e) { T x = *__p; s } for some invisible name __p.
Translation for the new form of condition in a while or for loop is analogous.
The "next generation" version where the type defaults to auto && is also proposed.
Range-based for-loops provide syntax that expands into the necessary bookkeeping and dereferencing needed to access the elements of a conventional begin()/end() traversable range.
There is a similar convention for checked access to pointees—the underlying objects of (smart) pointers and things like std::optional<>:
if (std::shared_ptr<T> sp = wp.lock()) { f(*sp); }
Using the new form of condition, this can be written:
if (T & x : wp.lock()) { f(x); }
Or, in next-gen form (where the type defaults to auto &&):
if (x : wp.lock()) { f(x); }
Similar to how range-based for hides the iterators and just lets you name the underlying object, a checked-dereference condition hides the pointer-like thing and just lets you name the underlying object.
while()
The new form of condition may also appear in a while loop:
while (T x : e) s
This translates to:
while (auto && __p = e) { T x = *__p; s }
This can be useful. For example, suppose we have:
std::optional<message> try_read(input &); void process(message);
then instead of:
while (std::optional<message> m = try_read(i)) { process(*m); }
we may write:
while (message m : try_read(i)) { process(m); }
Or, in next-gen form:
while (m : try_read(i)) { process(m); }
for()
Since a condition may also appear in a (traditional) for loop, a checked-dereference condition may be used there, too:
for (s1; T x : e1; e2) s2
translates to:
for (s1; auto && __p = e1; e2) { T x = *__p; s2 }
This form is not expected to be commonly used, but is easy to support consistently.
switch()
Since a checked-dereference condition would not make much sense for a switch statement's condition, allowing this use is not proposed (so there would have to be a separate condition grammar production).
The form if (T x : e) { ... }
may remind one of something like
monad_bind(e, [](T x){ ... })
or even
functor_map(e, [](T x){ ... })
However, these are ways to make additional values "in the monad/functor".
By contrast, the checked-dereference condition is for getting underlying values "out" of the indirection, and so is more akin to pattern matching. That is,
if (x : e) s1; else s2;
is comparable to a pattern match like (here shown in Haskell):
case e of Just x -> s1 Nothing -> s2
The author is planning a proof-of-concept implementation in Clang. Implementation experience for range-based and next-generation for suggests that the effort should be minimal.
None yet, but can be provided if there is interest.