June 17, 2017·4 min read·engineeringcppstandardsrangesconcepts

Ranges proposes dropping function-style concepts

P0651R0 replaces function-style concepts in the Ranges TS. Most changes are mechanical, but overloaded requirements need new names or narrower parameter lists.

A concept used in a requires clause can need empty parentheses in the Concepts TS design, while a placeholder use of the same concept omits them. Eric Niebler's June 17 P0651R0 proposes removing that inconsistency from the Ranges TS by switching to variable-style concepts. Most definitions can change mechanically, but a few overloaded names expose a deeper design choice.

The Ranges TS originally used function concepts consistently because some requirements had forms taking different numbers of arguments. Function overloading let those forms share a name. Variable concepts cannot be overloaded in that way, so the proposed simplification trades one kind of consistency for more explicit names where the arity matters.

I prefer fewer incidental spelling rules in generic interfaces. Concepts are supposed to make requirements easier to read, and remembering whether a predicate needs a function-call suffix adds another distinction unrelated to the algorithm's purpose. The interesting question is whether the replacement names still communicate the relationships a library needs.

Most definitions need a syntactic rewrite

For a concept with only one form, the change replaces a function returning a Boolean expression with a variable concept initialized by that expression. Uses in explicit constraints drop the empty parentheses. The requirement itself can remain the same.

The following self-contained illustration uses Concepts TS syntax to show the proposed variable form. It describes a simple compile-time size predicate, not a new standard-library concept.

template<class T>
concept bool SmallObject = sizeof(T) <= 16;

template<class T>
    requires SmallObject<T>
void process_small(const T&);

SmallObject<T> is the predicate expression. No function call suffix is needed to obtain its Boolean result. The definition still belongs to the TS concept system and is not ordinary C++17 variable syntax with a universally supported concept keyword.

Niebler gives Movable as the mechanical example, composing move construction, assignment and swapping requirements. The significant transformation is the declaration form and use-site spelling. It does not ask the library to invent a different meaning for moving an object merely to remove the parentheses.

Cross-type requirements need separate names

Some concepts have a unary form describing one type and a binary form describing a relationship between types. The paper uses equality comparison as an example. A one-argument requirement can relate a type to itself, while the two-argument requirement needs to describe comparison across the two types.

P0651R0 proposes a With suffix for the cross-type form. That yields names such as EqualityComparableWith, with precedent from the existing swap traits. The names preserve the semantic connection while avoiding an overloaded variable-concept declaration.

The same pattern affects swapping and total ordering. For relation concepts, Niebler instead identifies unused forms that can be removed without affecting the TS's uses. This is an important part of the proposal. It does not mechanically multiply names for every overload; it examines which forms the specified algorithms actually need.

Indirect callable requirements expose their arity

The indirect callable concepts are more complicated because they test combinations of iterator-associated types. According to the paper, the ordinary callable concepts naturally use parameter packs, while the indirect forms have fixed arities to cover those combinations. Their existing overloads include forms for different numbers of arguments.

Niebler observes that IndirectInvocable constrains the unary function used by for_each. He proposes keeping the needed form and naming it IndirectUnaryInvocable. A similar change makes the regular callable requirement used by projected explicitly unary. The predicate requirement also becomes IndirectUnaryPredicate after the related issue correction described in the paper.

These names are longer, and the paper acknowledges the cost. The alternative is to keep the function-style system that the Concepts TS already supports. The proposal favors simpler use-site syntax and avoiding dependence on a language feature that might be removed, while accepting more explicit names for the retained forms.

The TS should not dictate the language design by accident

The design tension is visible in Stroustrup's January concepts paper. Its discussion of multiple notations explains why function concepts allow overloading and why ordinary variables and functions have different use syntax. P0651R0 evaluates that generality against the actual needs of one library specification.

Niebler reports that the interface changes have been implemented and tested in the Ranges reference implementation. That is useful implementation experience, while the paper remains a proposal to respecify the TS. It does not announce that all compilers or existing ranges libraries already use the new names. Its wording also updates algorithm signatures and iterator requirements, so adopting the naming decision requires consistent changes throughout the specification.

I favor examining the concrete uses before retaining an entire spelling mechanism for them. Here the proposal can handle most concepts mechanically, separate cross-type relationships with With and narrow callable forms to the arities actually needed. The resulting specification gives up overloaded concept names while preserving explicit requirements for the algorithms that consume them.