Placeholder in return type of abbreviated template function

180 views
Skip to first unread message

Joseph Mansfield

unread,
Jan 29, 2015, 5:40:33 AM1/29/15
to conc...@isocpp.org
As of N4333, is it possible to declare an abbreviated template function like so?

OutputIterator copy(InputIterator first, InputIterator last, OutputIterator d_first);

Or possibly:

auto copy(InputIterator first, InputIterator last, OutputIterator d_first) -> OutputIterator;

My main concern is the placeholder in the return type. From what I can see, the answer is no, as it's not listed in 7.1.6.5/1, although I'm not sure why this is so. Sure, a return type alone can't be deduced, but we can still check it when explicitly instantiating the template. And if the placeholder in the return type is the same as any in the parameter list, it should share the same invented template parameter, and so then can be deduced.

Please let me know if I'm missing anything. And if we can't do this, is the best solution to use `decltype` in the trailing return type?

auto copy(InputIterator first, InputIterator last, OutputIterator d_first) -> decltype(d_first);

Thanks.

Ville Voutilainen

unread,
Jan 29, 2015, 6:00:36 AM1/29/15
to conc...@isocpp.org
On 29 January 2015 at 12:40, Joseph Mansfield <sftr...@gmail.com> wrote:
> As of N4333, is it possible to declare an abbreviated template function like
> so?
>
> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator
> d_first);
>
> Or possibly:
>
> auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
> -> OutputIterator;

No. [dcl.spec.constr]/1 forbids a constrained-type-specifier in such contexts,
as you noticed yourself.

> My main concern is the placeholder in the return type. From what I can see,
> the answer is no, as it's not listed in 7.1.6.5/1, although I'm not sure why
> this is so. Sure, a return type alone can't be deduced, but we can still
> check it when explicitly instantiating the template. And if the placeholder
> in the return type is the same as any in the parameter list, it should share
> the same invented template parameter, and so then can be deduced.

The question is whether the deduction should then happen before the constraint
check or after the constraint check, which affects whether the overload SFINAEs
or not. To make things simple, that question doesn't arise because such uses
are simply forbidden.

> Please let me know if I'm missing anything. And if we can't do this, is the
> best solution to use `decltype` in the trailing return type?
>
> auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
> -> decltype(d_first);

Looks like a reasonable solution to me.

Bjarne Stroustrup

unread,
Jan 29, 2015, 2:40:22 PM1/29/15
to conc...@isocpp.org
Looks like a reasonable *workaround* to me. Maybe

auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
-> InputIterator;

should mean that.

Ville Voutilainen

unread,
Jan 29, 2015, 2:50:30 PM1/29/15
to conc...@isocpp.org
On 29 January 2015 at 21:40, Bjarne Stroustrup <bja...@stroustrup.com> wrote:
>>> auto copy(InputIterator first, InputIterator last, OutputIterator
>>> d_first)
>>> -> decltype(d_first);
>>
>> Looks like a reasonable solution to me.
>>
>
> Looks like a reasonable *workaround* to me. Maybe
>
> auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
> -> InputIterator;
>
> should mean that.


Maybe. It's no secret that I think I should be able to write

OutputIterator x = foo.begin();

eventually, so I envision these restrictions to be temporary in nature.

Bjarne Stroustrup

unread,
Jan 29, 2015, 2:53:21 PM1/29/15
to conc...@isocpp.org

On 1/29/2015 2:50 PM, Ville Voutilainen wrote:
> On 29 January 2015 at 21:40, Bjarne Stroustrup <bja...@stroustrup.com> wrote:
>>>> auto copy(InputIterator first, InputIterator last, OutputIterator
>>>> d_first)
>>>> -> decltype(d_first);
>>> Looks like a reasonable solution to me.
>>>
>> Looks like a reasonable *workaround* to me. Maybe
>>
>> auto copy(InputIterator first, InputIterator last, OutputIterator d_first)
>> -> InputIterator;
>>
>> should mean that.
>
> Maybe. It's no secret that I think I should be able to write
>
> OutputIterator x = foo.begin();
I agree
>
> eventually, so I envision these restrictions to be temporary in nature.
>

Yes, I think that a bit of experience with concepts will convince people
about this, but the important thing right now is to get the document
voted on by the NBs.

Tomasz

unread,
Jan 29, 2015, 5:00:37 PM1/29/15
to conc...@isocpp.org

> My main concern is the placeholder in the return type. From what I can see,
> the answer is no, as it's not listed in 7.1.6.5/1, although I'm not sure why
> this is so. Sure, a return type alone can't be deduced, but we can still
> check it when explicitly instantiating the template. And if the placeholder
> in the return type is the same as any in the parameter list, it should share
> the same invented template parameter, and so then can be deduced.

The question is whether the deduction should then happen before the constraint
check or after the constraint check, which affects whether the overload SFINAEs
or not. To make things simple, that question doesn't arise because such uses
are simply forbidden.

The question still remains open, as the function that uses the constrained argument can still use a return type deduction in form of auto/decltype(auto). So if I have following function:
template<typename T>
concept Range
{
  requires(T  t) {
    {t.begin()} -> auto;
    {t.end()} -> auto;
  }
}

auto begin(Range const& c)
{
  return c.begin();
}
Is the constrain checking for the Range argument performed before return type deduction. so the overload will be removed for set of viable candidates if passed object does not declare begin member. Or the return type is deduced before constrain check, so passing an entity that does not declare begin() member will cause hard error instead of substitution failure?
 

Tomasz

unread,
Jan 29, 2015, 5:28:49 PM1/29/15
to conc...@isocpp.org

I think that the applicable would be 7.1.6.4 [dcl.spec.auto] p12
Return type deduction for a function template with a placeholder in its declared type occurs when the
definition is instantiated even if the function body contains a return statement with a non-type-dependent
operand. [ Note: Therefore, any use of a specialization of the function template will cause an implicit
instantiation. Any errors that arise from this instantiation are not in the immediate context of the function
type and can result in the program being ill-formed. — end note ]
My understanding is that the function templates are not instantiated until they are actually called. That requires them tho be selected as best viable candidate by overload resolution and argument deduction. As the constrain checking is the part of the overload resolution process, the return type deduction is performed after constrain check. Is my implementation rigth?

For the original question, I think that the declared function will not use return type deduction, as the terse form:

auto copy(InputIterator first, InputIterator last, OutputIterator d_first) -> OutputIterator;
Is equivalent to (same concept name = same type):
template<InputIterator I, OutputIterator O>
auto copy(I first, I last, O d_first) -> O;
As type O can be deduced from function arguments.

However if we declare following function:
Iterator begin(Range const&);
Or:
auto begin(Range const&) -> Iterator;
Then someone may expect, that it will perform return type deduction. However would be equivalent to following long form:
template<Iterator I, Range R>
I begin(R const&);
As you may see, this template still does not use return type deduction, but it will not callable without providing first template argument on the calls site (begin<I>(c)) as it cannot be deduced from parameters.

In the light of above examples, is the restriction that forbids uses of ConceptName placeholder in return type necessary?

Joseph Mansfield

unread,
Jan 30, 2015, 4:47:17 AM1/30/15
to conc...@isocpp.org
Perhaps a placeholder in a return type should not introduce a template parameter, but if it is the same as a placeholder in the parameter list then it should require that the return type and the parameter are deduced to the same type. We can always deduce the return type of a function from the return expression as long as we are able to deduce the parameter types.

Tomasz

unread,
Jan 30, 2015, 5:29:17 AM1/30/15
to conc...@isocpp.org
Checking the requirement on deducted return type will require return type to be deduced during overload resolution, as the failure in fulfilling constrain should eliminate function from set of viable candidates. But in current wording, the failure in deduction of return type is hard error, instead of SFINEA. So with that resolution, if during the overload resolution for one of the candidate return type deduction will result in invalid declaration,hard error will occur, instead of candidate being removed from overload set.

Andrew Sutton

unread,
Jan 30, 2015, 9:01:22 AM1/30/15
to conc...@isocpp.org
> Checking the requirement on deducted return type will require return type to
> be deduced during overload resolution, as the failure in fulfilling
> constrain should eliminate function from set of viable candidates. But in
> current wording, the failure in deduction of return type is hard error,
> instead of SFINEA. So with that resolution, if during the overload
> resolution for one of the candidate return type deduction will result in
> invalid declaration,hard error will occur, instead of candidate being
> removed from overload set.

This is my understanding, and why I very much dislike deduced return
types. The totally conflate the separation of interface and
implementation, which turns out to have some negative impact on other
language features.

My advice? Declare the return type of your functions.

Incidentally, I think that the "same-template-parameter" rule needs to
be extended for return types so that this:

Iterator f(Iterator, Iterator);

is equivalent to this:

template<Iterator I> I f(I, I);

I think that was mentioned in a previous email though.

Andrew

Tomasz

unread,
Jan 30, 2015, 10:29:03 AM1/30/15
to conc...@isocpp.org


W dniu piątek, 30 stycznia 2015 15:01:22 UTC+1 użytkownik Andrew Sutton napisał:
> Checking the requirement on deducted return type will require return type to
> be deduced during overload resolution, as the failure in fulfilling
> constrain should eliminate function from set of viable candidates. But in
> current wording, the failure in deduction of return type is hard error,
> instead of SFINEA. So with that resolution, if during the overload
> resolution for one of the candidate return type deduction will result in
> invalid declaration,hard error will occur, instead of candidate being
> removed from overload set.

This is my understanding, and why I very much dislike deduced return
types. The totally conflate the separation of interface and
implementation, which turns out to have some negative impact on other
language features.

My advice? Declare the return type of your functions.

I think that the problem only occurs if we need to instantiate return type during the overload resolution and that is not the case for auto/decltype(auto). I base this on the following paragraph: 7.1.6.4 [dcl.spec.auto] p12

Return type deduction for a function template with a placeholder in its declared type occurs when the
definition is instantiated even if the function body contains a return statement with a non-type-dependent
operand. [ Note: Therefore, any use of a specialization of the function template will cause an implicit
instantiation. Any errors that arise from this instantiation are not in the immediate context of the function
type and can result in the program being ill-formed. — end note ]
There is not mention of return type deduction in overload resolution and template argument deduction chapters (checked in implementation paper http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3582.html). I think that it means that the following function:
auto begin(Concept c);
Will not give cause you any problem, if the Concept c will guarantee that in-station of the begin function will not result in error, effectively requiring that the function is not under constrained, which is natural. But if we allow something like this:
Iterator begin(Concept c);
And the concept name as the placeholder will still indicate that the return type is deduced, then we will have problems, as the deduction need to be performed during overload resolution, when we do concept checks.

I think that some people will i mediately say that the solution would be simple, just check the the constrain on argument, before deducing return type. That is not true, as we can have a multi type constrain, that will both reffer to arguments and return type. Also there is a second problem - inability to rewrite such abbreviated from into full declaration:
template<typename C>
  requires Concept<C> && Iterator<??>
auto begin(C c);
I hilighted the questionable part - we need to be able to reffer by identifier to deduced return type. In short, I think that attempt to allow concept name in return type involve return type deduction will failure: eigther we will get a poisonous declaration that will change function invocation in to hard error (if we do not change the rules) or create monstrous set of rules, that will work in some cases, but if they will fail, then nobody will understand why.

Incidentally, I think that the "same-template-parameter" rule needs to
be extended for return types so that this:

  Iterator f(Iterator, Iterator);

is equivalent to this:

  template<Iterator I> I f(I, I);

I think that was mentioned in a previous email though.

Why not just apply all parameters rules for the return type declaration. I mean that if I write something like follows:
String lexical_cast(Number); // Both String and Number are concepts
That would be equivalent to
template<String S, Number N>
S lexical_cast(Number);
No return type deduction in the function. It just need to be invoked with one explicitly provided function parameter that denotes return type. But the following:
auto lexical_cast(Number) -> String;
Will require to provide me both template parameters, as it would be equivalent to:
template<Number N, String S>
auto lexical_cast(N) -> S;

And if I write:
OutputIterator copy(InputIterator, InputIterator, OutputIterator);
That would mean:
template<OutputIterator O, InputIterator I>
O copy(I, I, O);

I see no need for special rules for concept name in return type versus parameters. Also this would be still compatible with placeholder{name} syntax:
OutputIterator{O} copy(InputIterator{I}, I, O); // OutputIterator is first template parameter
auto opy(InputIterator{I}, I, OutputIterator{O}) -> O; //InputIterator is first template parameter

Ryan Burn

unread,
Nov 16, 2015, 2:22:15 PM11/16/15
to SG8 - Concepts
Have these rules changed at all? The latest version of gcc allows you to place constraints on return-types (http://melpon.org/wandbox/permlink/LRrm5sqkWy7faqUO

Bjarne Stroustrup

unread,
Nov 16, 2015, 4:53:06 PM11/16/15
to conc...@isocpp.org


On 11/16/2015 2:22 PM, Ryan Burn wrote:


On Friday, January 30, 2015 at 9:01:22 AM UTC-5, Andrew Sutton wrote:
> Checking the requirement on deducted return type will require return type to
> be deduced during overload resolution, as the failure in fulfilling
> constrain should eliminate function from set of viable candidates. But in
> current wording, the failure in deduction of return type is hard error,
> instead of SFINEA. So with that resolution, if during the overload
> resolution for one of the candidate return type deduction will result in
> invalid declaration,hard error will occur, instead of candidate being
> removed from overload set.

This is my understanding, and why I very much dislike deduced return
types. The totally conflate the separation of interface and
implementation, which turns out to have some negative impact on other
language features.

My advice? Declare the return type of your functions.

Incidentally, I think that the "same-template-parameter" rule needs to
be extended for return types so that this:

  Iterator f(Iterator, Iterator);

is equivalent to this:

  template<Iterator I> I f(I, I);

I think that was mentioned in a previous email though.  

Andrew

This doesn't work now?


Have these rules changed at all? The latest version of gcc allows you to place constraints on return-types (http://melpon.org/wandbox/permlink/LRrm5sqkWy7faqUO
--
You received this message because you are subscribed to the Google Groups "SG8 - Concepts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to concepts+u...@isocpp.org.
To post to this group, send email to conc...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/concepts/.

Casey Carter

unread,
Nov 16, 2015, 5:01:26 PM11/16/15
to SG8 - Concepts
On Monday, November 16, 2015 at 3:53:06 PM UTC-6, Bjarne Stroustrup wrote:

This doesn't work now?

It didn't in January when this thread was current; enabling placeholders in variable declarations and deduced return types was one of the changes made in Lenexa in response to an NB comment on the PDTS. The wording was somewhat of a last minute addon before the June telecon that approved the TS, so it's not as well integrated into the rest of the TS as it could have otherwise been.

For Ryan, yes these rules have changed. Take a look at http://wg21.link/n4549 for gory detail.

Andrew Sutton

unread,
Nov 16, 2015, 5:38:40 PM11/16/15
to SG8 - Concepts

It is intended to be supported.

Suggestions on how to improve wording are welcome.


--
Reply all
Reply to author
Forward
0 new messages