<..> An abbreviated function template is equivalent to a function template (14.6.6) whose template-parameter-list includes one invented template-parameter for each occurrence of a placeholder in the parameter-declaration-clause, in order of appearance <..>
template<R T1, R T2> auto different(T1 t1, T2 t2);
template<R T> auto same(T t1, T t2);
If you see the same typename in a parameter list, it's the same type.
it would be very confusing to have something that looks like a typename, but doesn't quite act like one.
You can always use full template syntax to express either one:
template<R T1, R T2> auto different(T1 t1, T2 t2);
template<R T> auto same(T t1, T t2);
--
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Well, that is exactly what i thought about reason to make it this way. But in my opinion, this is clearly wrong.If you see the same typename in a parameter list, it's the same type.
Agreed.it would be very confusing to have something that looks like a typename, but doesn't quite act like one.Not agreed.I think, if one knows that there are such things as type constraints,
It's a source of endless confusion, I've seen many people asking the same question for the last years.
You can search https://groups.google.com/a/isocpp.org/forum/?fromgroups#!forum/concepts for relevant threads.The main motivation is a shorter notation for functions taking two iterators of the same type - void foo(Iterator begin, Iterator end) or similar, they are considered a common case.
This motivation is pretty unconvincing given the arrival of ranges and Iterator/Sentinel pairs,
It's a source of endless confusion, I've seen many people asking the same question for the last years.You can search https://groups.google.com/a/isocpp.org/forum/?fromgroups#!forum/concepts for relevant threads.The main motivation is a shorter notation for functions taking two iterators of the same type - void foo(Iterator begin, Iterator end) or similar, they are considered a common case.
So I come with a conservative idea: how about a sign to opt-out such
a requirement:
auto f(R a, R' b); // a single quote after the concept, reads "R prime"
// you can have R'', R''', etc.; the number of ' has no limit, but you won't
// overuse it, right?
at the end of the day, we have to pick whether the terse syntax should under constrain (by allowing them to be two different types) or over constrain (by requiring them to be the same type). While I personally prefer the former, it isn't a strong enough preference to push any farther than bringing it up with those immersed in getting Concepts out the door (which I did a while back).
In my mind, the point of terse template function syntax is that the user doesn't care if it's a type constraint or not. That you could take `auto foo(T t1, T t2)` and turn it into `auto foo(C t1, C t2)` (where C is a concept that matches T), and the only visible difference (besides which types are allowed) is that you can't get a function pointer to it. As far as the user is concerned, it works just like `int` or `vector<T>`. It looks like a typename and it behaves like a typename.
One example for both cases:
compare(InputIterator begin1, InputIterator end1, InputIterator begin2)
Here, begin2 does not need to have the same type as begin1 and end1. In fact,
one could argue that even begin1 and end1 need not be the same type, so long
as they're comparable to one another. But the most common case is that begin1
and end1 have the same type.
In fact, the requirement that begin1 and end1 be comparable should be part of
the concept signature, which means a function that would accept two different
iterator types would need to be:
compare(InputIterator begin1,
InputIteratorComparableTo<decltype(begin1)> end1,
InputIterator begin2)
And since the second iterator has a different declaration from the first, this
cannot be used as an argument for supporting the case where they need to be
different.
If each occurance of the InputIterator concept is separate we can still write:bool compare(InputIterator begin1, decltype(begin1) end1, InputIterator begin2);
But what I said above still applies: since you used a different declaration,
it does not support the argument that two InputIterator uses in the same
declaration should be different.
Which case would cause most surprise if used incorrectly?
a) the case where an API constrains the types more than it should
b) the case where an API fails to constrain the types as much as it should
you used a different declaration
Given that one of the stated goals of Concepts is to give us better error
messages because types are checked against the concept prior to the overload
selection
But what I said above still applies: since you used a different declaration,
it does not support the argument that two InputIterator uses in the same
declaration should be different.I'm not making an argument that two iterators should be different. Also, i am not talking about STL's "common case" which is not common, but the only possible. And let's not forget about RangesTS, and STL2, where that "common case" is not so common.
I'm saying that type checking has nothing to do with defining parameters of a function. It's just none of its business. User defines parameters. User defines their types. User decides, if they are of the same type or not.merge(Container src_a, Container src_b, Container output)Do i want `src_a', `src_b' and `output' be of the same type? No.
- But, because `Container' placed where types are placed, someone might think that it's a type. That is confusing for that someone, lets make types the same. - Okay, but what is about ones who is not confused, who actually learned the language? Why make them pay and suffer? Is ones who don't learned prioritized over ones who learned?
And how is that even possible that someone will try to use a function like above, without actually knowing what is a `Container'?
If you have a docs, you'll read that `merge' is a template function that accepts three objects, types of which meet a requirements of the `Container' concept. And if you doesn't have a docs you will go to source code and see for yourself that `Container' is not a type.
I say, there is no source of confusion. Right now, confusion is not in the heads of a users.
Oh, and that terse syntax hides that it's a template function. - But that is not a problem. It is purpose of that syntax, to get rid of template boilerplate, no?
I used different declaration to show that i don't need InputIterator to force same type for begin and end, if i need same types i define them to be the same. Just like i do right now, by the way, by writingtemplate <typename I, typename O>O copy(I begin, I end, O out)I don't need obscure rules to define begin and end to be of the same type. I can use template-introduction for that, which is perfectly suitable. And by allowing constrained parameters with same constraints be of different types, it's will be also easier to write functions where you just doesn't care about actual types. And if it's not enough, you always have usual template syntax, for both cases. Cake for you, and cake for you. Everyone is happy.
On Tuesday 24 November 2015 21:54:57 Andrew Tomazos wrote:
> On Tue, Nov 24, 2015 at 6:10 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> > In my mind, the point of terse template function syntax is that the user
> > *doesn't care* if it's a type constraint or not. That you could take
> > `auto foo(T t1, T t2)` and turn it into `auto foo(C t1, C t2)` (where C
> > is a concept that matches T), and the only visible difference (besides
> > which types are allowed) is that you can't get a function pointer to it.
> > As far as the user is concerned, it works just like `int` or `vector<T>`.
> > It looks like a typename and it behaves like a typename.
>
> If we think of a concept as a compile-time interface (where a pure virtual
> class is a run-time interface), then we would expect:
>
> class Animal { ... };
> class Dog : Animal { ... };
> class Cat : Animal { ... };
>
> void f(const Animal& a, const Animal& b);
>
> Dog dog;
> Cat cat;
>
> f(dog, cat);
>
> This argues for allowing different types.
That's an example of polymorphism but it's showing the same type: Animal. So I
don't agree with you that it argues for allowing different types.
On Wednesday 25 November 2015 19:47:33 Andrew Tomazos wrote:
> concept Animal ...;
> class Dog { ... };
> static_assert(Dog conforms to Animal);
> class Cat { ... };
> static_assert(Cat conforms to Animal);
>
> void f(Animal a, Animal b);
>
> Dog dog;
> Cat cat;
>
> f(dog, cat);
>
> I argue that both this example and the previous are similar in important
> ways.
>
> The mechanics of derived-to-base conversions, base class subobjects,
> reference binding, etc, etc - are secondary to how the language should work
> at a conceptual level. They are just a means to an end.
In this case, the function f would be a template. And that's where things
become blurry, because if the arguments are templates, there's no automatic
casting to a base class.
I would say that your `merge` function here is underconstrained. Why? Because even if those different `Container` instances could deduce different types, those three different types need to have something in common. Namely, that the value type from `src_a` and `src_b` are convertible to the value type for `output`.
So if you actually applied all of the constraints that your function needs, you couldn't use terse syntax.
And I think that leads to an interesting question. How often do you have a function which:
1) Takes two arguments that can be of different types.
2) Those two arguments are constrained by the same concept.
3) There are no additional constraints on those types that would prevent you from using terse syntax.
I'd bet that 90% of the time that #1 and #2 are true, there's probably some additional constraint that your function requires of those two types.
Take your suggestion for `compare`. The first range and second range need not be the same types. But, they do need to have comparable value types. And a properly constrained template function will apply that constraint, so that users who pass the wrong things will get reasonable errors. And you can't apply such a constraint with terse syntax.
Why make the language more confusing to users? Why make people have to learn esoteric rules?
Somebody will be inconvenienced no matter what you do. Why is your side the one that deserves to have the special syntax? Indeed, you seem to argue exactly why they shouldn't: because they're the ones who are willing to learn and use arcane rules. And therefore, they will be less inconvenienced by such arcane rules than if you do it the other way around.
And how is that even possible that someone will try to use a function like above, without actually knowing what is a `Container'?
The same way that people use std::vector without knowing that the type it takes actually has minimum requirements.
The purpose of terse syntax is to minimize template boilerplate in the most common and useful of cases.
inline _OI
copy(_II __first, _II __last, _OI __result)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_II>)
__glibcxx_function_requires(_OutputIteratorConcept<_OI,
typename iterator_traits<_II>::value_type>)
__glibcxx_requires_valid_range(__first, __last);
return (std::__copy_move_a2<__is_move_iterator<_II>::__value>
(std::__miter_base(__first), std::__miter_base(__last),
__result));
}
<..> to minimize template boilerplate <..>. It is not intended to be a full-fledged replacement for declaring template functions.
There will always be cases where you need to use full template syntax.
Nobody has contested that full template syntax can do either one. Nobody has suggested that having terse syntax use one way means that the other way becomes impossible.
Seems somewhat inconsistent to treat unconstrained types one way and constrained exactly the opposite
So if you actually applied all of the constraints that your function needs, you couldn't use terse syntax.And I think that leads to an interesting question. How often do you have a function which:
1) Takes two arguments that can be of different types.
2) Those two arguments are constrained by the same concept.
3) There are no additional constraints on those types that would prevent you from using terse syntax.I'd bet that 90% of the time that #1 and #2 are true, there's probably some additional constraint that your function requires of those two types.Take your suggestion for `compare`. The first range and second range need not be the same types. But, they do need to have comparable value types. And a properly constrained template function will apply that constraint, so that users who pass the wrong things will get reasonable errors. And you can't apply such a constraint with terse syntax.That's why i am unhappy with current situation, i can apply additional constraints.
InputIterator_Sentinel {I, S} bool compare(I begin1, S end1, InputIterator begin2)
requires EquallyComparable<decltype(*begin1), decltype(*begin2)>
&& /* any additional constraints */
and if you need and actual type of begin2, and you don't want to use decltype, you can use usual template syntax.
void foo(Thingy t)
{
Thingy j = <expr>;
static_assert(is_same<decltype(t), decltype(j)>::value, "Why should this ever be false?");
}
Why make the language more confusing to users? Why make people have to learn esoteric rules?That is why i am here! Why? Rule that is made to make me, a user, less confused, made me confused even before it came into effect!
Somebody will be inconvenienced no matter what you do. Why is your side the one that deserves to have the special syntax? Indeed, you seem to argue exactly why they shouldn't: because they're the ones who are willing to learn and use arcane rules. And therefore, they will be less inconvenienced by such arcane rules than if you do it the other way around.That is not an arcane rule.
Again, what concept does (should do)? It checks, that object of type T behaves as you need, not that everything that behaves the same way have the same type.
Ducks, i brought you some amount of ducks. They look like a ducks, they quack like a ducks, and you treat them like a ducks. But then, i take duck consumes off, and you see that this is a cat, this is a dog, and this is a horse! And quacking sounds was made with little speakers! What you will do, put costumes back on, and say, "Nope, they're all ducks"?
When GCC 6.0 will be released, will this function be made to use terse syntax? Or __glibcxx_* macro will be redefined?
What about Eric Niebler's and Casey Carter's STL2 version?template <InputIterator I, Sentinel<I> S, WeaklyIncrementable O>
requires
models::IndirectlyCopyable<I, O>
tagged_pair<tag::in(I), tag::out(O)>
copy(I first, S last, O result)
{
for (; first != last; ++first, ++result) {
*result = *first;
}
return {__stl2::move(first), __stl2::move(result)};
}It uses concepts already, but, where is a terse syntax here?
auto copy(InputIterator first, Sentinel<InputIterator> last, OutputIterator result) requires IndirectlyCopyable<InputIterator, OutputIterator>
Is someone going to use it? Where is the most common and useful of cases applies?
STL1 is not the only now, and most probably, it will not use terse syntax.
Nobody has contested that full template syntax can do either one. Nobody has suggested that having terse syntax use one way means that the other way becomes impossible.Suppose you writing a library with a lot of template functions. Some of them take arguments of the same, type. Some of them not. Suppose you want to constrain all you functions properly, so you use terse syntax for parameters with same type and usual template syntax for parameters that share same concept but not required to have same type.
Will you be writing it like that
template <R T2>auto maybe_same(R p1, T2 p2)auto same(R p1, R p2)or for the sake of consistency, like that:template <R T1, R T2>auto maybe_same(T1 p1, T2 p2)template <R T>auto same(T p1, T p2)?
Without same-type rule it could be written like that:auto maybe_same(R p1, R p2)R {T} auto same(T p1, T p2)Aaand consistent version looks kinda, better than others.. maybe just more familliar. But first version, i don't like it at all.
From: Andrew Tomazos Sent: Wednesday, November 25, 2015 3:14 PM Reply To: std-pr...@isocpp.org Subject: Re: [std-proposals] Re: Question about P0121R0 (Concepts TS) |
From: Andrei L Sent: Wednesday, November 25, 2015 7:09 PM |
Reply To: std-pr...@isocpp.org |
Subject: Re: [std-proposals] Question about P0121R0 (Concepts TS) |
And actually, you bring up a very important point. Namely, if we do things your way, we have to throw around a lot of `decltype(variableName)` to get the typename of a variable if you use terse syntax.
Why is your confusion more important than someone else's confusion?
Somebody's going to be confused either way. It's either going to be the people who are acutely aware that using a constraint makes the function a template, or it's going to be the people who aren't aware of that.
As far as I'm concerned, the former group can handle it better than the latter.
It requires knowing:
1) That you're using a constraint rather than a typename, despite all appearances to the contrary.
2) That the rules for using a constraint in a parameter list are different from the rules of using a typename in a parameter list.
Ducks, i brought you some amount of ducks. They look like a ducks, they quack like a ducks, and you treat them like a ducks. But then, i take duck consumes off, and you see that this is a cat, this is a dog, and this is a horse! And quacking sounds was made with little speakers! What you will do, put costumes back on, and say, "Nope, they're all ducks"?You're arguing my case for me.
If the point of putting a duck costume on a cat/dog/horse is that it behave like a duck, then make it behave like a duck. While it's wearing the duck costume, it should be as indistinguishable as possible from a duck.
If I have to know whether it's a cat or dog or whatever, then why make it look like a duck at all?
If the point of terse template syntax is to make a constrained template function act like a non-template function, then it should make it act like a non-template function.
Constraints used in place of typenames should therefore act like typenames. That is essentially how the Concepts TS makes it work.
I'm not sure about whether `Sentinel<InputIterator>` can work as a constrained-type-name. But other than that though, what's the problem?
Prove that the current syntax is not "the most common and useful of cases".
STL1 is not the only now, and most probably, it will not use terse syntax.
... again, so what? Does it need to?
Right now, the algorithms and iterator libraries can't effectively use concepts at all. This is due to the fact that it wasn't designed with such constraints in mind, and imposing reasonable constraints now will likely break a lot of people's code.
I wouldn't expect someone to write the latter "for the sake of consistency"
Without same-type rule it could be written like that:
auto maybe_same(R p1, R p2)R {T} auto same(T p1, T p2)Aaand consistent version looks kinda, better than others.. maybe just more familliar. But first version, i don't like it at all.
I can't say that I agree that this is "consistent".
What about Eric Niebler's and Casey Carter's STL2 version?template <InputIterator I, Sentinel<I> S, WeaklyIncrementable O>
requires
models::IndirectlyCopyable<I, O>
tagged_pair<tag::in(I), tag::out(O)>
copy(I first, S last, O result)
{
for (; first != last; ++first, ++result) {
*result = *first;
}
return {__stl2::move(first), __stl2::move(result)};
}It uses concepts already, but, where is a terse syntax here?
auto copy(InputIterator first, Sentinel<InputIterator> last, OutputIterator result) requires IndirectlyCopyable<InputIterator, OutputIterator>
I'm not sure about whether `Sentinel<InputIterator>` can work as a constrained-type-name. But other than that though, what's the problem?
2015-11-26 22:50 GMT+05:00 Nicol Bolas <jmck...@gmail.com>:I'm not sure about whether `Sentinel<InputIterator>` can work as a constrained-type-name. But other than that though, what's the problem?The problem is that same-type-terse-syntax is not used. Most useful case did not applied.
Prove that the current syntax is not "the most common and useful of cases".Ha! That is not working like that! Prove that it is.
STL1 is not the only now, and most probably, it will not use terse syntax.
... again, so what? Does it need to?
Right now, the algorithms and iterator libraries can't effectively use concepts at all. This is due to the fact that it wasn't designed with such constraints in mind, and imposing reasonable constraints now will likely break a lot of people's code.You just proved that it is not.
I wouldn't expect someone to write the latter "for the sake of consistency"Do you write curly braces on the same or on the next line? Do you jump between styles? That is, you choose one style and you write using that style.
Without same-type rule it could be written like that:auto maybe_same(R p1, R p2)R {T} auto same(T p1, T p2)Aaand consistent version looks kinda, better than others.. maybe just more familliar. But first version, i don't like it at all.
I can't say that I agree that this is "consistent".By consistent version i meant example with `template <typename>`s
On Thursday, November 26, 2015 at 11:50:46 AM UTC-6, Nicol Bolas wrote:What about Eric Niebler's and Casey Carter's STL2 version?template <InputIterator I, Sentinel<I> S, WeaklyIncrementable O>
requires
models::IndirectlyCopyable<I, O>
tagged_pair<tag::in(I), tag::out(O)>
copy(I first, S last, O result)
{
for (; first != last; ++first, ++result) {
*result = *first;
}
return {__stl2::move(first), __stl2::move(result)};
}It uses concepts already, but, where is a terse syntax here?
auto copy(InputIterator first, Sentinel<InputIterator> last, OutputIterator result) requires IndirectlyCopyable<InputIterator, OutputIterator>
I'm not sure about whether `Sentinel<InputIterator>` can work as a constrained-type-name. But other than that though, what's the problem?
According to N4553, a partial-concept-id is perfectly acceptable as a placeholder in a parameter declaration of an abbreviated function template - there's even an example that includes such a usage. However, there's nothing in N4553 to indicate that the replacement of placeholder parameter types with invented template parameters in the parameter-declaration-clause also happens in an associated requires-clause. That does seem like a consistent extension to me, and it would allow terse syntax to be applicable to many more cases. Someone should propose it.
It's obvious you meant that, but that doesn't make it consistent as far as I'm concerned.
I wouldn't expect someone to write the latter "for the sake of consistency"
Do you write curly braces on the same or on the next line? Do you jump between styles? That is, you choose one style and you write using that style.
Are we even talking about the same thing? Because your statement here doesn't seem to address the point.
You're still introducing a template parameter, just with a different syntax that doesn't use the word "template". In fact, this form of `same` doesn't even use abbreviated function template syntax at all. So it doesn't trigger that part of the compiler.
Are we even talking about the same thing?
You will not be able to write everything in terse syntax, and there's no getting around that fact. Because of that, you will have to select your syntax based on what you can use for your particular use case. You will have functions that can use terse syntax and you will have functions that can't.
Consistency of using terse syntax is not achievable. Therefore, what does it matter if some cases require falling back to full template syntax?
My point was that the current Concepts TS does not in any way preclude you from writing this using abbreviated function template syntax. It doesn't matter if this particular function doesn't make use of type consistency in abbreviated functions. So long as it doesn't preclude you from writing this example, it's meaningless.
template <InputIterator I, Sentinel<I> S, WeaklyIncrementable O>
requires
models::IndirectlyCopyable<I, O>
tagged_pair<tag::in(I), tag::out(O)>
copy(I first, S last, O result)
And it's not even that hard. If you want to prove which is more common, just go through the standard library and ranges TS algorithms, and see how many of them could use abbreviated function template syntax under the current concepts TS and how many could use it under your proposed changes.
If you are correct about what is "the most common and useful of cases", then evidence shouldn't be that hard to find, right?
Who has the burden of proof here? Concepts TS is an ISO standard that is being implemented as we speak. Therefore, the person requesting a change is the one upon whom the burden of proof rests. Not to mention, providing evidence always makes your case stronger, even if the burden of proof isn't on you.
auto copy(InputIterator first, Sentinel<InputIterator> last, OutputIterator result) requires IndirectlyCopyable<InputIterator, OutputIterator>
Hello,
I've been reading through the paper and noticed this:<..> An abbreviated function template is equivalent to a function template (14.6.6) whose template-parameter-list includes one invented template-parameter for each occurrence of a placeholder in the parameter-declaration-clause, in order of appearance <..>
Is this means that in such code:template <typename T>
concept bool R = CheckSomething<T>;
auto foo(R a, R b) { /* ... */ }parameters `a' and `b' have the same type? And if so, why? What is the rationale behind that?
--