Checked-dereference conditions (original) (raw)
- Document Number: N4127
- Date: 2014-07-20
- Project: Programming Language C++, Evolution Working Group
- Reply-to: Eelis van der Weegen (wg21@contacts.eelis.net)
I. Introduction
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 toif (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.
II. Motivation
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.
III. Use with 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);
}
IV. Use with 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.
V. Use with 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).
VI. Relation to monadic bind / pattern matching
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
VII. Implementation
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.
VIII. Proposed Wording
None yet, but can be provided if there is interest.