Concept type-name introducer is a proposed new mechanism to introduce multiple template arguments with a single expressionMergeable{In,In2,Out}
Out merge(In, In2, Out);or eventuallytemplate<Mergeable{In,In2,Out}>
Out merge(In, In2, Out);In any case the result will be the same astemplate<class In, class In2, class Out>
requires Mergeable<In,In2,Out>
Out merge(In, In2, Out);
The main motivation for an introducer is to "reduce the verbosity of requires clause".Second motivation is that "this concept type name introducer is necessary to avoid cut&paste and macros."However, the main motivation is served not by the introducer itself, but by introduction of tailored concepts.
The second motivation is understandable - if we have such a repeated pattern some people might use a macro and others will cut and paste.That said, the name introducers come with problems on their own.
- Completely new syntax that needs learning. A bit better with template<> as it is already the place for name introduction.
- New syntax not for a new feature!
- Completely "flat" and inflexible - no composition or decomposition. As a result all type information about the names is actually always hidden. A bit better in the template<> variant, because one can add other names/constrains.
- Extremely confusing "dual use" of concepts - sometimes they look like a template instantiation, sometimes they look like something b/w initializer list and structured bindings.
- The syntax visually overloads already quite common syntax - Something{}, class Something{};
Given all this, the difference b/w standard notation with tailored concept VS name introducer does not seem big and significant enough.
Still, undeniably there is repetition that might lead to some errors.Can we have minimum solution to that alone? Can we make template<> optional if requires is used?In other wordsrequires Mergeable<In,In2,Out>
Out merge(In, In2, Out);Will introduce In, In2 and Out.
On Monday, June 25, 2018 at 3:20:29 PM UTC-4, mihailn...@gmail.com wrote:Concept type-name introducer is a proposed new mechanism to introduce multiple template arguments with a single expressionMergeable{In,In2,Out}
Out merge(In, In2, Out);or eventuallytemplate<Mergeable{In,In2,Out}>
Out merge(In, In2, Out);In any case the result will be the same astemplate<class In, class In2, class Out>
requires Mergeable<In,In2,Out>
Out merge(In, In2, Out);
The main motivation for an introducer is to "reduce the verbosity of requires clause".Second motivation is that "this concept type name introducer is necessary to avoid cut&paste and macros."However, the main motivation is served not by the introducer itself, but by introduction of tailored concepts.
The second motivation is understandable - if we have such a repeated pattern some people might use a macro and others will cut and paste.
That said, the name introducers come with problems on their own.
- Completely new syntax that needs learning. A bit better with template<> as it is already the place for name introduction.
- New syntax not for a new feature!
I don't think repeating the same criticism is helpful.
- Completely "flat" and inflexible - no composition or decomposition. As a result all type information about the names is actually always hidden. A bit better in the template<> variant, because one can add other names/constrains.
- Extremely confusing "dual use" of concepts - sometimes they look like a template instantiation, sometimes they look like something b/w initializer list and structured bindings.
- The syntax visually overloads already quite common syntax - Something{}, class Something{};
Given all this, the difference b/w standard notation with tailored concept VS name introducer does not seem big and significant enough.
Still, undeniably there is repetition that might lead to some errors.Can we have minimum solution to that alone? Can we make template<> optional if requires is used?In other wordsrequires Mergeable<In,In2,Out>
Out merge(In, In2, Out);Will introduce In, In2 and Out.How is this not new syntax? Because it doesn't use a new symbol? It's using existing symbols in new ways, just like {} does. For example:
1. `Name<...>` always means "specialize this template". But that's not happening here. Or rather it is, but that's not all that's happening here.2. `<>` doesn't declare identifiers without explaining what those identifiers actually are. That is, without `typename`, `template`, or an actual type's name.
3. This new form of `<>` only works in a `requires` clause.
4. `requires` now has a third meaning. It's similar to the original meaning, but because it's applied as a prefix, it now performs double-duty as a template header. At least `template<Mergable>` looks like a template header.
Outside the debate of the you-know-that syntax, I think thisdemand is easy to fulfill. I think we can allow multi-argumentconstrained parameters like this:template <Mergeable In In2 Out, class Other>Out merge(In, In2, Out);
Outside the debate of the you-know-that syntax, I think thisdemand is easy to fulfill. I think we can allow multi-argumentconstrained parameters like this:template <Mergeable In In2 Out, class Other>Out merge(In, In2, Out);
On Wednesday, June 27, 2018 at 5:44:13 AM UTC-4, Zhihao Yuan wrote:Outside the debate of the you-know-that syntax, I think thisdemand is easy to fulfill. I think we can allow multi-argumentconstrained parameters like this:template <Mergeable In In2 Out, class Other>Out merge(In, In2, Out);"Functions" whose arity are non-obvious are (in my opinion) an unfortunate aspect of certain languages.
K&R C gives us the perfectly usable idea of a list of identifiers that introduce parameters; it even allows further attributes of the parameters to be declared afterwards:
int f(a, b, c)
int a, b, c;
{ return a + b + c; }
Lines (statements ending in semicolons) could be used to apply constraints.
template <In, In2, Out, Other>
{ Mergeable <In, In2, Out>; typename Other; }
Out merge(In, In2, Out);
On Monday, June 25, 2018 at 11:22:43 PM UTC+3, Nicol Bolas wrote:On Monday, June 25, 2018 at 3:20:29 PM UTC-4, mihailn...@gmail.com wrote:
- Completely "flat" and inflexible - no composition or decomposition. As a result all type information about the names is actually always hidden. A bit better in the template<> variant, because one can add other names/constrains.
- Extremely confusing "dual use" of concepts - sometimes they look like a template instantiation, sometimes they look like something b/w initializer list and structured bindings.
- The syntax visually overloads already quite common syntax - Something{}, class Something{};
Given all this, the difference b/w standard notation with tailored concept VS name introducer does not seem big and significant enough.
Still, undeniably there is repetition that might lead to some errors.Can we have minimum solution to that alone? Can we make template<> optional if requires is used?In other wordsrequires Mergeable<In,In2,Out>
Out merge(In, In2, Out);Will introduce In, In2 and Out.How is this not new syntax? Because it doesn't use a new symbol? It's using existing symbols in new ways, just like {} does. For example:No, {} is used in a new context, creating new construct. No new constructs are created here, simply using-is-a-declaration.
1. `Name<...>` always means "specialize this template". But that's not happening here. Or rather it is, but that's not all that's happening here.2. `<>` doesn't declare identifiers without explaining what those identifiers actually are. That is, without `typename`, `template`, or an actual type's name.The same argument can be made about both name introducers and even shorthand notation.
template<template<typename> class X> concept C2 = true;template<C2 X> void f2(); //< WTF is X, must look the conceptI am willing to argue, this is actually more clear in that particular case:requires C2<X> void f2(); //< X is what is written b/w the <> of the concept!The fact that a concept, which is a template, is used as such is a huge plus for requires in general.
3. This new form of `<>` only works in a `requires` clause.It is not about <>, it is exclusively about 'requires'.
Alternatively, if we would feel better, might be template Mergeable<In,In2,Out> Out merge(In, In2, Out);.
4. `requires` now has a third meaning. It's similar to the original meaning, but because it's applied as a prefix, it now performs double-duty as a template header. At least `template<Mergable>` looks like a template header.But requires is already part of the template header!
I don't see any of these particularly troubling. Yes, there is some new context-sensitive interpretation, which is still better then new syntax for a bonus feature.But lets equate the new syntax (introducers) to these new rules in terms of how troubling they are. These new rules give us more power, full power.
They also eliminate the question "what is a name introducer". This topic no longer exist. Yes, new question arises - where are these template arguments coming from, but that is a trivial question to answer.
On Wednesday, June 27, 2018 at 12:44:13 PM UTC+3, Zhihao Yuan wrote:Outside the debate of the you-know-that syntax, I think thisdemand is easy to fulfill. I think we can allow multi-argumentconstrained parameters like this:template <Mergeable In In2 Out, class Other>Out merge(In, In2, Out);
I actually like that idea.
I merely used some CS knowledge here. Any finite
automaton which can accept
Sortable A B
is able to consume
Sortable A
as a prefix, but not all finite automatons which accept
Sortable{A, B}
also consume
Sortable A
So I guess it’s not too unsound to treat “Sortable A B” as,
grammatically, a generalization to “Sortable A”?
Yes, I know, 1-tuple doesn’t need braces. I will be
convinced when I can write (int 1).
--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________
From: Nicol Bolas <jmck...@gmail.com>
Sent: Thursday, June 28, 2018 9:44 PM
template <Mergeable In In2 Out, class Other>
Out merge(In, In2, Out);
I actually like that idea.
So, your definition of "new syntax" does not include "list of identifiers without separators in places where that's not normally allowed".
[…]
I'm not exactly enamored with curly braces myself. But you have to admit that it's a lot less novel of syntax compared to "list of identifiers with no separators that somehow introduce template parameter names based on a concept". At least curly-braced-bound lists are a thing in C++ that people are used to. They may have different meaning, but it is a normal feature of the language to see `{identifier, identifier, identifier}` in code.
You also skipped "when can we use that ability of template instantiation to create template parameters?" Or more to the point, "why can I only use it in a prefixed `requires` clause?"This is reason #1 why I dislike template introduction syntax: it creates new syntax that can only be used in a narrow way. Herb's syntax at least has the benefit of being consistent, rather than this giant wart you can sometime use.On Wednesday, June 27, 2018 at 6:40:40 AM UTC-4, mihailn...@gmail.com wrote:On Wednesday, June 27, 2018 at 12:44:13 PM UTC+3, Zhihao Yuan wrote:Outside the debate of the you-know-that syntax, I think thisdemand is easy to fulfill. I think we can allow multi-argumentconstrained parameters like this:template <Mergeable In In2 Out, class Other>Out merge(In, In2, Out);I actually like that idea.So, your definition of "new syntax" does not include "list of identifiers without separators in places where that's not normally allowed".I'm starting to get the impression that your position is not based on problems with "new syntax" in general or even terse templates as an idea. It seems more like "not curly braces!" That you'll accept any solution as long as it doesn't involve curly braces (and probably parentheses or square brackets).
I'm not exactly enamored with curly braces myself. But you have to admit that it's a lot less novel of syntax compared to "list of identifiers with no separators that somehow introduce template parameter names based on a concept". At least curly-braced-bound lists are a thing in C++ that people are used to. They may have different meaning, but it is a normal feature of the language to see `{identifier, identifier, identifier}` in code.
By contrast, `identifier identifier identifier` doesn't happen in C++. Not as a list of things, at least.
| From: mihailn...@gmail.com Sent: Friday, June 29, 2018 4:18 AM To: ISO C++ Standard - Future Proposals Reply To: std-pr...@isocpp.org Subject: [std-proposals] Re: Concepts: Investigating implicit type-name introduction |
template<Concept T U, class A>where do I put const?
On Fri, 29 Jun 2018, 13:53 Tony V E, <tvan...@gmail.com> wrote:template<Concept T U, class A>where do I put const?You don't... do you? Maybe I'm missing out on something fundamental, but const doesn't make sense to me in this context.
template<Concept T const U, class A>Would the committee be forced to make a final decision oneast const vs const west?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAC%2B0CCPqv5eoVLgXcuEaCSj%3D0qeV-UJ0%3Db8xEEknmkfPJMuw2w%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUbR9HrEh6vDv3n47v93ss0e7GdR6Nh69VLjEU%2BL%3DwqBig%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CA%2BOm%2BSga5T5bZWFKEkHth%2B%2ByvpPMu8%2B0Bd5obgU8Ozyhc%2B3HFA%40mail.gmail.com.
On 4 July 2018 at 04:48, Nicol Bolas <jmck...@gmail.com> wrote:
> Also, we get to use `ConceptName auto/typename/template` when constraining
> explicit template parameters. This unpleasantness is mitigated because
There is no 'ConceptName typename' nor 'ConceptName template' in that proposal.
> From: Ville Voutilainen <ville.vo...@gmail.com>
> Sent: Thursday, July 5, 2018 2:55 AM
>
> > Yes, I would have preferred Herb's syntax, because it means I don't
> > have to use `decltype`, but I can live with this.
>
> The biggest reason for this paper was to try and find an approach that more
> people can live with.
>
This is the piece I dislike the most. A wholesome proposal
must base on real code and real demand. So far, neither
Ranges nor Text_view has any demand for anything what
P1141R0 proposes. It looks like at this point we are not
just designing by committee, but also designing for
committee.
> From: Nicol Bolas <jmck...@gmail.com>
> Sent: Friday, July 6, 2018 10:04 PM
>
>> A wholesome proposal
>> must base on real code and real demand. So far, neither
>> Ranges nor Text_view has any demand for anything what
>> P1141R0 proposes. It looks like at this point we are not
>> just designing by committee, but also designing for
>> committee.
>
> Pretty much everyone agrees that having terse syntax of some form is a good thing. Real demand for terse syntax of some form is undeniable; just ask actual users whether they want it or not.The only question which remains is how you spell it. The Concepts TS had one spelling, but it had some issues. This proposal resolves those issues in a way that most people can live with.
>
"Real demand for terse syntax of some form is undeniable"
!= " Real demand for terse syntax of any form is undeniable."
If the proposed syntax doesn't help Ranges, then where is
the demand?
template <Iterator I> constexpr void advance(I& i, difference_type_t<I> n);constexpr void advance(Iterator auto &i, different_type_t<decltype(i)> n);constexpr void advance(Iterator{I} &i, difference_type_t<I> n);Here is a roughly complete list of the
signatures that can benefit from P1141R0 in Ranges:
destroy_at
next (1 overload out of 4)
prev (1 overload out of 3)
operator==, operator!= of “unreachable”, 4 overloads
One might say that Ranges is complex -- how would he/she
prove it, show us a simple real-world Concept-enabled
library?
void sort(Cont& c);void sort(Range &&r, Comp<decltype(r)> comp = std::less<value_type<decltype(r)>{});> From: Nicol Bolas <jmck...@gmail.com>
> Sent: Saturday, July 7, 2018 10:13 AM
>
> I think the point you're trying to make is that applying the terse syntax to most Range algorithms wouldn't make them shorter. Or at least, not much.
Not making them simpler, or readable, or conveying more
precise information. From
template <Iterator I>
void advance(I& i, difference_type_t<I> n);
to
void advance(Iterator auto &i, different_type_t<decltype(i)> n);
, the latter is just cryptic.
> In some cases, the order of parameters directly forbids it.
>
> However, no terse syntax would help in those cases. Not the Concepts TS, not Bjarne's `template` prefix, nor Herb's `{}` syntax. Once the order of function parameters and template parameters has to differ, you lose the ability to use any terse syntax.
>
> So let's skip all of those. That's pretty much all of the algorithms.
We need to look at the right problem to find the right solution;
you cannot hold a solution, and tell me my problem doesn't
work.
Here is an example, from
template<InputIterator I, Sentinel<I> S, class Proj = identity,
IndirectUnaryPredicate<projected<I, Proj>> Pred>
bool all_of(I first, S last, Pred pred, Proj proj = Proj{});
to
template<InputIterator I, Sentinel<I> S, IndirectUnaryPredicate<I> Pred>
bool all_of(I first, S last, Pred pred);
template<InputIterator I, Sentinel<I> S, ProjectedUnaryPredicate<I> Proj Pred>
bool all_of(I first, S last, Pred pred, Proj proj);
, isn't the latter more readable?
> The idea with terse syntax is to keep the simple cases simple. Pretty much nothing in the Range TS is a simple case.
>
> On the other hand... the complexity of the Range system I think has really damaged the utility of terse syntax.
> [...]
> The point being that the more conceptualized a function becomes, the chance of it being able to use terse syntax of any form approaches 0.
I don't care a bit what terse syntax can do; I care only what
problem we can solve.
> The principal users of terse syntax will not be writers of generic libraries, but users of generic libraries.
That's an interesting way to formulate the problem, but before
I calling it hypothetical --
> And that suggests a potential problem: laziness. If users of simple cases get used to terse syntax, they may avoid doing things that stop them from being able to use terse syntax. So rather than write out the complex `sort` in ranges, they'd just write `void sort(RandomAccessRange auto &&r, auto comp = std::less<>{}, auto proj = std::identity{});` and just let it go at that.
> Whereas, if you force them to stick that `RandomAccessRange` in a template argument list, and deny them the ability to use `auto` in function parameters, then they have no choice but to spell it out explicitly. And since they're having to write it long-form, they may as well constrain it properly.
, you may as well find it's hard to defend.
> They're useful when you're writing a quick lambda to be passed somewhere. `[](UnsignedInteger auto i) {}` is going to be easier to digest than `[]<UnsignedInteger I> (I i) {...}`. Not massively shorter of course, but still more readable.
If I'm told that from
[]<typename I> (I i)
to
[](typename auto i)
is an simplification I will be very mad,
> From: Nicol Bolas <jmck...@gmail.com>
> Sent: Saturday, July 7, 2018 5:52 PM
>
> Also, it's not like we don't see this sort of thing with lambdas today: `[](auto &i, difference_type_t<decltype(i)> n);`. Now yes, with C++20 allowing template header syntax in lambdas, we may not need it anymore, but this idiom is hardly unfamiliar to users.
I never write that even in C++14, never.
>> Here is an example, from
>>
>> template<InputIterator I, Sentinel<I> S, class Proj = identity,
>> IndirectUnaryPredicate<projected<I, Proj>> Pred>
>> bool all_of(I first, S last, Pred pred, Proj proj = Proj{});
>>
>> to
>>
>> template<InputIterator I, Sentinel<I> S, IndirectUnaryPredicate<I> Pred>
>> bool all_of(I first, S last, Pred pred);
>>
>> template<InputIterator I, Sentinel<I> S, ProjectedUnaryPredicate<I> Proj Pred>
>> bool all_of(I first, S last, Pred pred, Proj proj);
>>
>> , isn't the latter more readable?
>
> Um... no? I don't know what that third template argument is supposed to be doing. Are Proj and Pred both types? Are they the same type? Different types? How does that work?
I had the same feeling when looking at Mergeable{I, S, O}
at the first time, but like you said, "It's very clear what it's
doing, once you understand the syntax." And my point is,
the latter is structurally simpler comparing to the former,
and that's how readability comes.
But comparing to
>> template <Iterator I>
>> void advance(I& i, difference_type_t<I> n);
,
>> void advance(Iterator auto &i, different_type_t<decltype(i)> n);
is structurally more complex and introduces words that
are unrelated to the context, and that's why I called it
cryptic, or you can find a better word for that.
> Terse syntax is syntactic sugar. By definition, it doesn't solve problems. Sugar makes things taste better; that's all it needs to do.
A proposal must solve problems. Lambda is a syntax
sugar and it solves the problem of not able to write
callbacks in expressions. Vittorio's P0915 solves the
problem of unable to express the constraints on
variables. Comparing to
auto item = producer.next();
static_assert(StandardLayoutType<decltype(item)>);
,
auto<StandardLayoutType> item = producer.next();
expresses the idea directly in code. Although I'm a little
surprised that nobody has thought about the most
obvious solution,
StandardLayoutType T;
T item = producer.next();
but I won't say that the proposed solution is not solving
a problem. What problem does P1141R0 solve?
--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________
--
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/R5J4t8pUi5E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
> From: Hubert Tong <hubert.rein...@gmail.com>
> Sent: Saturday, July 7, 2018 11:27 PM
>
> Loss of visual anchors can lead to less readability. What's noise to some are important eyecatchers to others.
> Unscoped enumerations and anonymous unions come to mind with regards to the Mergeable{I, S, O} syntax.
I hope that "Mergeable I S O" is doing better since this
structure is less often seen in the language.
>>
>> StandardLayoutType T;
>> T item = producer.next();
>
> Probably because it is unclear where T is bound to a concrete type in this syntax.
> Is the first binding privileged? Or is this a way of saying that all deductions of T must agree on the deduced type?
Or perhaps T has independent binding on each use?
All deductions of T must agree on the same type, just
like what
template<StandardLayoutType T>
void(T a, T b)
does.
template<StandardLayoutType T>
void func(int i = sizeof(T)) {...}StandardLayoutType T next = producer.next();StandardLayoutType{T} next = producer.next();StandardLayoutType T auto next = producer.next();
>> What problem does P1141R0 solve?
> Wait for it...
>
> auto item = producer.next();
> static_assert(StandardLayoutType<decltype(item)>);
>
> StandardLayoutType auto item = producer.next();
And I did not comment on that part, right? Of course
there can be a consistency argument to defend the
constraints on parameters, but P0915R0 did not do
that since this argument would be too weak.
On Sunday, July 8, 2018 at 1:14:00 AM UTC-4, Zhihao Yuan wrote:> From: Hubert Tong <hubert.rein...@gmail.com>
> Sent: Saturday, July 7, 2018 11:27 PM
>
> Loss of visual anchors can lead to less readability. What's noise to some are important eyecatchers to others.
> Unscoped enumerations and anonymous unions come to mind with regards to the Mergeable{I, S, O} syntax.
I hope that "Mergeable I S O" is doing better since this
structure is less often seen in the language.It's not seen "less often"; it is seen precisely never. It is completely unlikely any syntax in C++. A general sequence of N tokens, with no separator token between them, is something that appears nowhere in the entire C++ standard.Oh, and please note that I said "general sequence of". Yes, I know C++ does have places where identifiers follow one another. But when this happens, the grammar restricts how many are around; this is not defined by some language construct. `Identifier Identifier` can declare a variable if `Identifier` is a typename, but `Identifier Identifier Identifier` is always syntactic nonsense. So let's not get bogged down in pedantry here.Whenever C++ creates a new list of stuff, it always used C-style: there is some kind of token pair bracketing the list (`()` and `{}` in C, `<>` in C++), and each list item is separated from other list items by a comma token. That's how C and C++ work when a list can be arbitrarily long.
Clarifying which identifiers go with the concept and which are constrained by it would require more C++-like lists. Like:StandardLayoutType{T} next = producer.next();
Which of course is parseable syntax. And while it may be unfamiliar, it isn't fundamentally alien.