1. Examples
// Status quo template < InputIterator I , Sentinel < I > S , class T , class Proj = identity > requires IndirectRelation < ranges :: equal_to , projected < I , Proj > , T *> constexpr I find ( I first , S last , const T & value , Proj proj = {}); // P1754’s suggestion template < input_iterator I , sentinel_for < I > S , class T , class Proj = identity > requires indirect_relation < ranges :: equal_to , projected < I , Proj > , T *> constexpr I find ( I first , S last , const T & value , Proj proj = {});
// Status quo template < ForwardIterator I , Sentinel < I > S , class T , class Proj = identity , IndirectStrictWeakOrder < const T * , projected < I , Proj >> Comp = ranges :: less > constexpr I upper_bound ( I first , S last , const T & value , Comp comp = {}, Proj proj = {}); // P1754’s suggestion template < forward_iterator I , sentinel_for < I > S , class T , class Proj = identity , indirect_strict_weak_order < const T * , projected < I , Proj >> Comp = ranges :: less > constexpr I upper_bound ( I first , S last , const T & value , Comp comp = {}, Proj proj = {});
// Status quo template < InputIterator I1 , Sentinel < I1 > S1 , RandomAccessIterator I2 , Sentinel < I2 > S2 , class Comp = ranges :: less , class Proj1 = identity , class Proj2 = identity > requires IndirectlyCopyable < I1 , I2 > && Sortable < I2 , Comp , Proj2 > && IndirectStrictWeakOrder < Comp , projected < I1 , Proj1 > , projected < I2 , Proj2 >> constexpr I2 partial_sort_copy ( I1 first , S1 last , I2 result_first , S2 result_last , Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); // P1754’s suggestion template < input_iterator I1 , sentinel_for < I1 > S1 , random_access_iterator I2 , sentinel_for < I2 > S2 , class Comp = ranges :: less , class Proj1 = identity , class Proj2 = identity > requires indirect_copyable < I1 , I2 > && sortable < I2 , Comp , Proj2 > && indirect_strict_weak_order < Comp , projected < I1 , Proj1 > , projected < I2 , Proj2 >> constexpr I2 partial_sort_copy ( I1 first , S1 last , I2 result_first , S2 result_last , Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});
The underscores serve as spaces in the names, which in my opinion, make the concepts significantly
easier to read -- particularly in the case of
, which is quite a mouthful. I’ve
also applied the
concepts to my numeric algorithms proposal, and it’s helped to improve
that document’s readability there too. I imagine production code using the
concepts
will benefit even more than the spec.
1.1. Concerns and the status quo
I’ve seen many people express distaste for the proposed change side due to the fact that 'we’ve
always named our concepts using
'. We haven’t: we have a set of tables in the standard
library that outline requirements, and we name these requirements
, and so on.
While they do use
, those names are used to alias tables that appear only to aid in
understanding the standard prose, and don’t represent any standard identifier.
Secondly, it’s easy to come to the conclusion that because there’s a named requirement called
and a concept called
, of course the concept is just a way
for us to express our requirements in a compiler-checkable way. This is misguided: the concept
goes way deeper than the named requirement
. It is left as
an exercise to the reader to distinguish the differences between the two. (One should note that the
named requirement has been rebranded as Cpp17EqualityComparable to signify that it’s different to
the concept, and that the font face has changed from
to italics, which means that there
shouldn’t be any more names in the standard spelt using
.)
Finally, our current templates look like this:
template < class InputIterator > template < typename InputIterator > template < auto N > template < int N >
They do not look like this:
template < Class I > // error: template type parameter declared using 'class' or 'typename' template < Typename I > // error: template type parameter declared using 'class' or 'typename' template < Auto N > // error: template non-type parameter declared using 'auto' or an integral type template < Int N > // error: template non-type parameter declared using 'auto' or an integral type
In other words, my take on this, is that P1754 aims to restore what we’ve always done, which is to
use a synonym of
to declare a type parameter in
, and we name our type
parameter in
. When I pointed this out to Herb, he mentioned that this is already
articulated in §1.1.3.
1.2. Who’s co-authoring it?
All of the major authors for both concepts and ranges have co-authored P1754, and are unanimous that this should happen. Being the architects for these features, it is likely that they collectively and individually have the most experience of anyone with these names. We should solemnly acknowledge this experience, and at least heavily experiment with their proposal before digging our heels in and claiming that the aberration in the standard library should remain status quo.
2. Thoughts on the new concept names
P1754 also seeks to alter the spelling of a few concepts, as seen below.
Current | With P1754 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2.1. Favoured renamings
Reimagining
as
makes a lot of sense. I also welcome renaming
to
, but wonders if
's name should become
for
similar reasons.
It’s not clear if choosing to rename
to
was intentional, so
I’d like to provide motivation for this case, in the event it wasn’t. While
reads nicer than
, and we could argue that the
concepts belong to [indirectcallable] and the
concepts live in [alg.req], I think this is just
happenstance. For example, when describing the requirements for the C++17 numeric algorithms, I
needed to define a concept called
, which has an indirect callable
counterpart that would likely live in [indirectcallable]. It’s very tempting to name this
instead of
, which immediately blurs the
line between what’s
and what’s
. In light of that, to avoid confusion, I
encourage exactly one of
or
.
2.2. Bikeshedding
There are a few names that I’m not so keen on, and would like to see change, as shown below.
P1754’s name | My alternative | Comment |
---|---|---|
|
TBD
|
is the epitome of our fears when designing concepts (see the design ideals of [N3351]). As such, we should aim to avoid having in the IS*, as it sets a
dangerous precedent: if exists, then the case against becomes
a lot harder.
Since *At the very least, tangible names shouldn’t be prefixed with |
| or or or or or
|
Although the phrase ' models ' reads okay, it clashes with existing
standard type names, such as , , , ,
and . Despite there being only a few concepts that end with , and
despite most of them not really being necessary outside of defining other concepts, it is
possible that these concepts are mistaken for types. For example, a user might conclude
that is the standard spelling for a user-defined boolean type to avoid
clashing with any non-standard boolean types. After all, we’ve already set a precedent
here.
Corollary: Of the suggested alternatives, I prefer |
|
|
The needs to go, for similar reasons as .
As P1754 points out, We can, however, choose |
|
|
All types that model also model , so I find the name to
be misleading, since potentially refines . (Note that it’s
not clear to me how refinement works when disjunctions are involved.)
From [range.refinements], we see that "the This then frees up the name |
3. Conclusion
Some of the names, such as
and
are a bit controversial, and
should be reviewed, but I want to make my position unambiguously clear: I do not think that the
current P1754 names should be a showstopper for gaining consensus.
I understand that there’s concern around renaming these concepts -- and P1754’s authors are also
aware of this -- but I fear that the concern is misplaced. We should embrace the change that P1754
seeks to make, so that our code doesn’t become too squashed, like
.
At the very least, please write a non-trivial example with the new names, so that you’ve got something to back you up when you say "I don’t like it".