About P0341R0: parameter packs outside of templates

174 views
Skip to first unread message

Vicente J. Botet Escriba

unread,
Jun 4, 2016, 9:35:29 AM6/4/16
to Evolution Working Group mailing list, C++ Library Evolution Working Group, refle...@isocpp.org
Hi,


thanks Mike for working on this.


Viewing parameter packs as types is a way to introduce anonymous product
types in the language and I like this..


* Naming: parameter pack type versus product type

I don't know if using parameter pack type is the good term as this is no
more a parameter.


* Parameter pack declarations

I suspect that the easy_tuple constructor should be

template<typename ...U> easy_tuple(U && ...u) : t{u}... {}

I believe it is worth noting that

T ...t;

wouldn't declare a single member data t, but a pack of them. Anyway,
what will be decltype(t)? I believe that this has no sens and so T...
shouldn't be a type.

* Accessing a template parameter pack outside the template

I believe that there is an error in

return ith_argument<i>(tup....t& ...);

it should be

return ith_argument<i>(tup....t ...);

The user of ... followed by the . selector is a little noisy and
confusing. Why do we need to use ... to identify that t is is a pack.
What could the meaning of the following?

return ith_argument<i>(tup.t ...);



* Pack literals versus Type-Constructor

I see the proposed <> as a type-constructor operator. Not only for
literals. Currently we have only the builtin *, &, [], () and class
templates. With your proposal we will have also <>.

I was wondering if the following will be correct and what will be the
difference between T1 and T2 if any

template <class ...Ts>
using T1 = <Ts...>;

template <class ...Ts>
using T2 = Ts...;


IMO, only the first has a sens. I believe it is a good idea to see
<Ts...> as a type that has anonymous parameter pack type member data,
but it is not a parameter pack type.


* Pack values - What is the type of decltype ({a,b})?

I like also the proposal to associate a type to a braced-init-list,
having expressions that don't have a type is not good.
Clearly a pack literal is the good candidate.

Been able to unpack a parameter pack type is a must and will simplify a
lot of library interfaces.

distanceFromMe(calculateTargetCoordinates()...)


However, the result type of calculateTargetCoordinates in

<double, double> calculateTargetCoordinates();

should be a type. Is a parameter pack type a type?


In addition using the same operator that parameter packs would disable
to write the same code for a function returning a user defined type.

easy_tuple<double, double> calculateTargetCoordinates();

distanceFromMe(calculateTargetCoordinates()...); // compilation fails :(


This is why I believe that we would need another operator@ to transform
a Pack literal or user defined type to a parameter pack type that
doesn't conflict with the fold operator...

distanceFromMe( @ calculateTargetCoordinates() ...))

* Named packs

Some months ago we were discussing in c++-standard ml about the
possibility of using anonymous structs as return type of a multiple
return function,
but they have already a well defined meaning that doesn't allows us to
reuse them in different context to mean the same type.


struct {string topSong; person president; double avgTemp}
someFactsAboutYear(int year);


Having named packs goes exactly in this direction, avoiding the backward
compatibility issue.

<string topSong, person president, double avgTemp>
someFactsAboutYear(int year)

I will go even towards

<topSong(string), president(person), avgTemp(double) >
someFactsAboutYear(int year)

where topSong(string) is constructing a type with a specific accessor


* Access

I believe that we need a direct access to the elements and its size as
we have fot tuple-like types and as I'm proposing for product-types in
general (P0327R0).

I woudl like to have some builtin operators to access parameter packs by
index.

Some have proposed alternative syntax to get the nth element (type) of a
parameter pack [N]T


The same operators could be used for pack literals types once we conver
to a parameter pack type

using R = <T,U>;
static_assert(sizeof...(@R)==2,"");

Some have proposed alternative syntax to get the nth element (type) of a
parameter pack [N]T

static_assert(is_same<[0]@R,T>{},"");


* Iteration

The iterator based interface to access heterogeneous containers should
be compared with the more functional interface Boost.Hana offers and
would need a separated proposal.


* User-defined types

I don't understand what the following could mean

template <typename ...T> using easy_tuple<T...>... = <T...>;

I would like to be able to use the same unpack operator@ for pack
literals and for user defined product types.

<T,U> would have an implicit operator@ that give access to its elements
as a parameter pack type.

Don't having it would mean that algorithms can not manage both kind of
types.

Using a different operator@ would avoid the conflict with fold
expressions and so there will be no need for an user defined operator....

Instead of f(...t...); we should have

f(@t...);


Now the user needs just to define the *magic* operator@

template<typename ...T>
struct easy_tuple {
T... t;

T... & operator@() { return t; }
};

I say, *magic* because it doesn't return a type but a parameter pack type.
Note also that it return a reference to parameter pack type instead of a
parameter pack type of references.

Another simple tuple could be

template<typename ...T>
struct easy_tuple : <Ts...> {};

Now the operator@ is inherited from <Ts...>.

What @ could be. I believe that for extension the operator is giving
access to any one of the members. We use operator*() to dereference a
pointer or reference like class.
I will suggest the use of this operator*


f(*tpl...);



* Relation to Structured binding

If parameter pack types will be used to return multiple values from a
function, structured binding should work for them also.

< int, double> f();
auto [ a, b ] = f();

Wondering if structured binding shouldn't use <> instead of [] then.

auto < a, b > = f();


Vicente

Ville Voutilainen

unread,
Jun 5, 2016, 4:04:47 PM6/5/16
to Evolution Working Group mailing list, C++ Library Evolution Working Group, refle...@isocpp.org
On 5 June 2016 at 22:48, Michael Spertus <mike_s...@symantec.com> wrote:
> Thanks for the detailed feedback, Vicente. [Comments] below


Here's a quick comment, about

return ith_argument<i>(tup....t& ...);

Please provide a *very strong* rationale for introducing that
quadruple-dot. Sure, it "makes sense" as a mechanism
for saying that a member is a pack, but this is the most distasteful
syntax I've seen in quite a while. It's fairly unreadable.

Vicente J. Botet Escriba

unread,
Jun 5, 2016, 6:51:52 PM6/5/16
to Evolution Working Group mailing list, C++ Library Evolution Working Group, refle...@isocpp.org
Le 05/06/2016 à 21:48, Michael Spertus a écrit :

Thanks for the detailed feedback, Vicente. [Comments] below

 

-----Original Message-----
From: Ext [mailto:ext-b...@lists.isocpp.org] On Behalf Of Vicente J. Botet Escriba
Sent: Saturday, June 4, 2016 8:35 AM
To: Evolution Working Group mailing list <e...@lists.isocpp.org>; C++ Library Evolution Working Group <lib...@lists.isocpp.org>; refle...@isocpp.org
Subject: [Ext] About P0341R0: parameter packs outside of templates

 

Hi,

 

 

thanks Mike for working on this.

 

 

Viewing parameter packs as types is a way to introduce anonymous product types in the language and I like this..

 

 

* Naming: parameter pack type versus product type

 

I don't know if using parameter pack type is the good term as this is no

more a parameter.

[Bikeshed. I'm fine either way]

 

* Parameter pack declarations

 

I suspect that the easy_tuple constructor should be

 

   template<typename ...U> easy_tuple(U && ...u) : t{u}... {}

[I assume you are referring to the missing ... in front of U. Yes, that was a typo]

Yes.

I believe it is worth noting that

 

   T ...t;

 

wouldn't declare a single member data t, but a pack of them. Anyway,

what will be decltype(t)? I believe that this has no sens and so T...

shouldn't be a type.

[Agreed. Let me know if you saw anything in the paper that suggests otherwise]

I'm not sure you are confirm that it is not a type?
However a function can return it. This is why I believe that your parameter pack type T...t and <Ts...> t are different things. The later would declare a variable having  the type <Ts...>. You seam to propose that the access to this types is parameter pack expansion, but I believe that we need as for a user defined type use the operator@ that converts a <Ts...> into a parameter pack Ts...

 

* Accessing a template parameter pack outside the template

 

I believe that there is an error in

 

   return ith_argument<i>(tup....t& ...);

 

it should be

 

   return ith_argument<i>(tup....t ...);

[Can you elaborate. Don’t we want a reference to the argument?]

Yes, but I don't see the need to add &. When you pass an lvalue it converts directly to a reference, isn't it?

The user of ... followed by the . selector is a little noisy and

confusing. Why do we need to use ... to identify that t is is a pack.

What could the meaning of the following?

 

   return ith_argument<i>(tup.t ...);

[<rant>I hate it (and all other sigils) too and would love concepts with definition checking, but</rant> but wasn’t sure I could get rid of it. I seem to recall a discussion where Richard and I concluded it was necessary but don’t recall the details. I guess the question is whether we need to know if something is an unexpanded pack is required for parsing. If I can’t reconstruct or rediscover an example, I will drop any objection to that.]

Ok, we will see if it is needed to disambiguate or not.

 

 

* Pack literals versus Type-Constructor

 

I see the proposed <> as a type-constructor operator. Not only for

literals. Currently we have only the builtin *, &, [], () and class

templates. With your proposal we will have also <>.

 

I was wondering if the following will be correct and what will be the

difference between T1 and T2 if any

 

   template <class ...Ts>

   using T1 = <Ts...>;

 

   template <class ...Ts>

   using T2 = Ts...;

 

 

IMO, only the first has a sens. I believe it is a good idea to see

<Ts...> as a type that has anonymous parameter pack type member data,

but it is not a parameter pack type.

[Oops. I did it both ways in the paper. You are correct that the first was intented.]


Do you confirm that <Ts...> and Ts... are not the same thing?

 

* Pack values - What is the type of decltype ({a,b})?

 

I like also the proposal to associate a type to a braced-init-list,

having expressions that don't have a type is not good.

Clearly a pack literal is the good candidate.

 

Been able to unpack a parameter pack type is a must and will simplify a

lot of library interfaces.

 

   distanceFromMe(calculateTargetCoordinates()...)

 

 

However, the result type of calculateTargetCoordinates in

 

   <double, double> calculateTargetCoordinates();

 

should be a type. Is a parameter pack type a type?

[Yes. That is stated explicitly in the paper]

If Ts... is a type then we should be able to do


   template <class ...Ts>

   using T2 = Ts...;

    Ts... t;

    decltype (t)



 

 

In addition using the same operator that parameter packs would disable

to write the same code for a function returning a user defined type.

 

   easy_tuple<double, double> calculateTargetCoordinates();

 

   distanceFromMe(calculateTargetCoordinates()...); // compilation fails :(

 

 

This is why I believe that we would need another operator@ to transform

a Pack literal or user defined type to a parameter pack type that

doesn't conflict with the fold operator...

 

      distanceFromMe( @ calculateTargetCoordinates() ...))

[I may be a little dense here, but not sure what the issue is. Can you give a more detailed explanation/example?]


For me easy_tuple<double, double> and <double, double> should provide the same operator. I believe that easy_tuple<double, double> can not provide directly the expansion operator... and so  <double, double> should provide it neither. We need for both an operator@ that allows the parameter pack expansion.


 

* Named packs

 

Some months ago we were discussing in c++-standard ml about the

possibility of using anonymous structs as return type of a multiple

return function,

but they have already a well defined meaning that doesn't allows us to

reuse them in different context to mean the same type.

 

 

   struct {string topSong; person president; double avgTemp}

someFactsAboutYear(int year);

 

 

Having named packs goes exactly in this direction, avoiding the backward

compatibility issue.

 

   <string topSong, person president, double avgTemp>

someFactsAboutYear(int year)

[I think you are agreeing with the approach in the paper, right?]

Right.

 

I will go even towards

 

   <topSong(string), president(person), avgTemp(double) >

someFactsAboutYear(int year)

 

where topSong(string) is constructing a type with a specific accessor

[Would need to see a separate paper…]

Yes, I didn't  wanted to post this sentence yet.

 

* Access

[I am intrigued by these, but do any of them need to be part of this proposal?]

Maybe not. I see <Ts...> as a product type and having direct access to the elements seem a basic operation on them.
Of course we could use the ith_argument trick, but I believe compilers can do better.

I believe that we need a direct access to the elements and its size as

we have fot tuple-like types and as I'm proposing for product-types in

general (P0327R0).

 

I woudl like to have some builtin operators to access parameter packs by

index.

 

Some have proposed alternative syntax to get the nth element (type) of a

parameter pack [N]T

 

 

The same operators could be used for pack literals types once we conver

to a parameter pack type

 

     using R = <T,U>;

     static_assert(sizeof...(@R)==2,"");

 

Some have proposed alternative syntax to get the nth element (type) of a

parameter pack [N]T

 

     static_assert(is_same<[0]@R,T>{},"");

 

 

* Iteration

 

The iterator based interface to access heterogeneous containers should

be compared with the more functional interface Boost.Hana offers and

would need a separated proposal.

[I would be open to splitting off the iteration based interface into another proposal if the committee agrees]

 

* User-defined types

 

I don't understand what the following could mean

 

     template <typename ...T> using easy_tuple<T...>... = <T...>;

[This is a “special notation” to allow a user-defined type T to say what T… is. An alternate notation might be

 

template <typename …T> using = T…;

 

What I don't understand is easy_tuple<T…>…

I explicitly say that the actual proposal would be in a separate paper and that this is for understanding vision/roadmap, so we have time to fine tune the notation.

I believe that we need to consider user defined types from the beginning as we need to consider generic functions on these kind of types.

 

I would like to be able to use the same unpack operator@ for pack

literals and for user defined product types.

 

<T,U> would have an implicit operator@ that give access to its elements

as a parameter pack type.

 

Don't having it would mean that algorithms can not manage both kind of

types.

 

Using a different operator@ would avoid the conflict with fold

expressions and so there will be no need for an user defined operator....

 

Instead of f(...t...); we should have

 

f(@t...);

 

 

Now the user needs just to define the *magic* operator@

 

template<typename ...T>

struct easy_tuple {

     T... t;

 

     T... & operator@() { return t; }

};

 

I say, *magic* because it doesn't return a type but a parameter pack type.

Note also that it return a reference to parameter pack type instead of a

parameter pack type of references.

 

[Is operator@() vs the paper’s operator…() a bikeshed or is there a semantic distinction?

It is similar, but the result is not the same and I believe it should be applied to <Ts...> also. Otherwise we will be unable to write generic algorithms for <Ts...> and for easy_tuple<Ts...>, which I believe is desirable.

I have used operator@ here to make evident that it is not an expansion operator... but the operator that give the possibility to do pack expansions.
In your paper ... plays 3 roles. As parameter pack type expansion, as something used to disambiguate a parameter pact type member access in a dependent type and as a user defined operator to convert it to <Ts&...>.

Also, how do you propose indicating that easy_tuple<int, double>… is the same as <int, double>…?

I don't know what easy_tuple<int, double>… could be. Do you mean what is the result type of easy_tuple<int, double>::operator...()?
Could you clarify this part of the paper?

What’s in the paper, or something different?]

Another simple tuple could be

 

template<typename ...T>

struct easy_tuple : <Ts...> {};

 

Now the operator@ is inherited from <Ts...>.

 

What @ could be. I believe that for extension the operator is giving

access to any one of the members. We use operator*() to dereference a

pointer or reference like class.

I will suggest the use of this operator*

 

 

f(*tpl...);

 

[I’m still a little confused by your use of operator@. Do you have anything written up on it?]

No. There was a post in the std-proposal ML [1] about how to unpack tuples to value sequences


template<typename ...Args)
func
(Args&& ...pack)
{
 
auto rev_agg = ...; //Some reverse-aggregate.

 
auto test1 = make_tuple(func(rev_agg)...); //Compile error; nothing in the expression is a value sequence.
 
auto test2 = make_tuple(func([*]rev_agg)...); //Success. Treats `rev_agg` as though it were a parameter pack.
 
auto test3 = make_tuple(func(rev_agg, pack)...); //Success. `rev_agg` gets no special treatment. Every call to `func` gets the same `rev_agg`, just as it would in current code.
 
auto test4 = make_tuple(func([*]rev_agg, pack)...); //Success. Unpacks both `rev_agg` and `pack`. Only works if the two are the same size, just like for two parameter packs.
}
[*]rev_agg was one of the proposed syntax for your UDT operator...

There were also

rev_agg~




[2] was a library ideas to unpack tuples.

[1] RFC: Unpacking tuples to value sequences
[2] invoke and unpacking tuple-like type instances

 

* Relation to Structured binding

 

If parameter pack types will be used to return multiple values from a

function, structured binding should work for them also.

 

     < int, double> f();

     auto [ a, b ] = f();

 

Wondering if structured binding shouldn't use <> instead of [] then.

 

     auto < a, b > = f();

[ <> doesn’t seem right because <ajl;ksdf> expressions are types, but maybe curly braces is most consistent with the paper?

auto {a, b} = f();]

If <int, double> is a type, what could be the more natural pattern matching for <int, double>?
It is true that we have also that  {1, 2.0} is a literal having type <int, double>.

Anyway, we would need to be able to do structured binding for <Ts...> as we do for tuple-like types , aggregates, c-arrays.

Vicente

Vicente J. Botet Escriba

unread,
Jun 6, 2016, 5:06:43 PM6/6/16
to Evolution Working Group mailing list, C++ Library Evolution Working Group, refle...@isocpp.org
Le 06/06/2016 à 03:46, Michael Spertus a écrit :

Hi Vicente,

A parameter pack is a type, but a pack expansion is not. See specific comments below for more details.

Best,

 

Mike

 

PS. Will you be in Oulu? I think some of these points may be easier to resolve in person than in many comment iterations.

Yes Michael, I will and you are completely right, it will be much easier. These exchanges have however help me to understand the details of the proposal and to identify where I believe than an alternative approach could be considered in Oulu.

 

 

From: Ext [mailto:ext-b...@lists.isocpp.org] On Behalf Of Vicente J. Botet Escriba
Sent: Sunday, June 5, 2016 5:52 PM
To: Evolution Working Group mailing list <e...@lists.isocpp.org>; C++ Library Evolution Working Group <lib...@lists.isocpp.org>; refle...@isocpp.org
Subject: Re: [Ext] About P0341R0: parameter packs outside of templates

 

Le 05/06/2016 à 21:48, Michael Spertus a écrit :

Thanks for the detailed feedback, Vicente. [Comments] below

 

-----Original Message-----
From: Ext [mailto:ext-b...@lists.isocpp.org] On Behalf Of Vicente J. Botet Escriba
Sent: Saturday, June 4, 2016 8:35 AM
To: Evolution Working Group mailing list <e...@lists.isocpp.org>; C++ Library Evolution Working Group <lib...@lists.isocpp.org>; refle...@isocpp.org
Subject: [Ext] About P0341R0: parameter packs outside of templates

 

Hi,

 

 


For me easy_tuple<double, double> and <double, double> should provide the same operator. I believe that easy_tuple<double, double> can not provide directly the expansion operator... and so  <double, double> should provide it neither. We need for both an operator@ that allows the parameter pack expansion.

Again, note that user-defined types are not specifically being proposed except as a possible future. Having gotten that disclaimer out of the way, note that tuple<double, double> cannot act exactly like a parameter pack does without breaking compatibility. E.g.,the following example is legal C++14, and only the second argument of g is expanded by the ...

 

  template<typename ...Ts> void f(Ts ...ts) { h(g(tuple<Ts...>(), ts)...); }

That is why the paper speculates that user-defined types will need to be prefaced by a sigil (either … as in the paper or @ as IIUC you are suggesting) before they can be expanded. However, for an actual pack, like those already in the language as well as those proposed in the paper, no sigil is required.


I'll not insist more on UDT, but as UDT will need an additional operator, I believe that <Ts...> will need it also even if it is not needed in this particular case because there is no backward compatibility issue. We need something coherent and homogeneous.


 

* User-defined types

 

I don't understand what the following could mean

 

     template <typename ...T> using easy_tuple<T...>... = <T...>;

[This is a “special notation” to allow a user-defined type T to say what T… is. An alternate notation might be

 

template <typename …T> using = T…;

 

What I don't understand is easy_tuple<T…>…

I explicitly say that the actual proposal would be in a separate paper and that this is for understanding vision/roadmap, so we have time to fine tune the notation.

I believe that we need to consider user defined types from the beginning as we need to consider generic functions on these kind of types.

I’m inclined to think they can (and should) be separated. I think that simply making packs work and be constructible outside of templates is an unadulterated good even if user-defined classes are not supported (much like how current packs that are limited to template parameters have proven to be a great feature of C++11). I’m not sure that I see how the paper can limit potential opportunities around generic functions because existing C++11 parameter packs, which don’t require a sigil to expand, would need to be accommodated in the same way.

 

Nevertheless, I did put some thoughts in the paper about future extensions to user-defined types just in case there were interactions that we should be considering now…

I will not be against separating the UDT part if <Ts...> has an operator@ that enable the parameter pack expansion.

 

I would like to be able to use the same unpack operator@ for pack

literals and for user defined product types.

 

<T,U> would have an implicit operator@ that give access to its elements

as a parameter pack type.

 

Don't having it would mean that algorithms can not manage both kind of

types.

 

Using a different operator@ would avoid the conflict with fold

expressions and so there will be no need for an user defined operator....

 

Instead of f(...t...); we should have

 

f(@t...);

 

 

Now the user needs just to define the *magic* operator@

 

template<typename ...T>

struct easy_tuple {

     T... t;

 

     T... & operator@() { return t; }

};

 

I say, *magic* because it doesn't return a type but a parameter pack type.

Note also that it return a reference to parameter pack type instead of a

parameter pack type of references.

 

[Is operator@() vs the paper’s operator…() a bikeshed or is there a semantic distinction?

It is similar, but the result is not the same and I believe it should be applied to <Ts...> also. Otherwise we will be unable to write generic algorithms for <Ts...> and for easy_tuple<Ts...>, which I believe is desirable.

I have used operator@ here to make evident that it is not an expansion operator... but the operator that give the possibility to do pack expansions.

The … prefix is a notation of Richard’s to indicate that something can be expanded, so I think that is the same as your operator@. If it turns out to be necessary, perhaps yours is a better notation than the …. that multiple people have found ugly.

When I use operator@ is to mean that we need to find out a good name. I'm not proposing to a new operator@ and even less to add a new encoding character.


In your paper ... plays 3 roles. As parameter pack type expansion, as something used to disambiguate a parameter pact type member access in a dependent type and as a user defined operator to convert it to <Ts&...>.

But as a prefix, it means what your operator@ does, right? Bikeshedding as above, I’m not opposed to using @ rather than … as a prefix.

I'm not completely against the name operator...(), but I would prefer something else. What I'm trying to explain is that the same operator is also needed for <Ts...>.

easy_tuple<Ts..> tu;
f(tu...); // compile fail as we would need first to enable the parameter pack expansion
f(...tu...); // Ok, unambiguous

<Ts...> t;
f(t...); // compile fail as we would need first to enable the parameter pack expansion
f(...t...); // Ok, unambiguous



Also, how do you propose indicating that easy_tuple<int, double>… is the same as <int, double>…?

I don't know what easy_tuple<int, double>… could be. Do you mean what is the result type of easy_tuple<int, double>::operator...()?
Could you clarify this part of the paper?

That is a “special notation” inspired by my reading of Richards operator…() Clang branch (but don’t blame him for any of my misinterpretations J). I have not proposed a type for it. I have played around with this question a bit, and fear that what I would like and what I can get are not the same (one of many reasons why I am not formally proposing pack expansions of user-defined types in this paper…). Fortunately, since operator…() is not actually proposed in this paper, I am happy to remain agnostic as to whether it has a return type or is just a “special notation.”

Ok. We can forget it for the time being.

Vicente
Reply all
Reply to author
Forward
0 new messages