Permit Structured Binding in Reverse

127 views
Skip to first unread message

Antonio Perez

unread,
May 15, 2017, 5:49:18 AM5/15/17
to ISO C++ Standard - Future Proposals
Structured binding is an extremely useful concept because it allows the efficent and intuitive unpacking of data-structures into their individual values, and it works on a wide range of data structures. Not only does it work on tuples and pairs, but it also works on structs defined by the programmer without any additional effort on their part. I propose making this language feature more complete by allowing structured binding to take place in reverse:

tuple a = {1, 3.7}; //Creates a tuple of type tuple<int, double> with values 1 and 3.7
tuple b
= [1, 3.7]; //Ditto

 Where is this useful?
The above example isn't particularly useful, HOWEVER here's a more real usage case:

[double, double] GetAngleVector(double x)
{
   
return [cos(x), sin(x)];
}

//All of these would be 100% valid:
tuple t
= GetAngleVector(2.7);
pair p
= GetAngleVector(2.7);
array
<double, 2> arr = GetAngleVector(2.7);
auto [x,y] = GetAngleVector(2.7);

struct Point2D {
   
double x,y;
};
Point2D point = GetAngleVector(2.7);

Here, the function returns a structured-binding that will automatically map to whatever it's assigned to without issue. 


Something similar could be achieved in standard C++17, although it's unseemly and difficult to read:
tuple<double, double> GetAngleVector(double x)
{
   
return {cos(x), sin(x)};
}

//All of these would be 100% valid:
pair p
= std::make_from_tuple<pair<double, double>, tuple<double,double>>(GetAngleVector(2.7));
array
<double, 2> arr = std::make_from_tuple<std::array<double, 2>, tuple<double,double>>(GetAngleVector(2.7));

struct Point2D {
   
double x,y;
};
Point2D point =
std::make_from_tuple<Point2D, tuple<double,double>>(GetAngleVector(2.7))


Ville Voutilainen

unread,
May 15, 2017, 6:37:50 AM5/15/17
to ISO C++ Standard - Future Proposals
On 15 May 2017 at 12:49, Antonio Perez <ant...@perezexcelsior.com> wrote:
> Structured binding is an extremely useful concept because it allows the
> efficent and intuitive unpacking of data-structures into their individual
> values, and it works on a wide range of data structures. Not only does it
> work on tuples and pairs, but it also works on structs defined by the
> programmer without any additional effort on their part. I propose making
> this language feature more complete by allowing structured binding to take
> place in reverse:
>
> tuple a = {1, 3.7}; //Creates a tuple of type tuple<int, double> with values
> 1 and 3.7
> tuple b = [1, 3.7]; //Ditto
>
> Where is this useful?
> The above example isn't particularly useful, HOWEVER here's a more real
> usage case:
>
> [double, double] GetAngleVector(double x)
> {
> return [cos(x), sin(x)];
> }
>
> //All of these would be 100% valid:
> tuple t = GetAngleVector(2.7);
> pair p = GetAngleVector(2.7);
> array<double, 2> arr = GetAngleVector(2.7);
> auto [x,y] = GetAngleVector(2.7);


Interesting. The way I looked at that idea was like this: "we want to
make it easy to allow
initializing an object from an object of tuple-like type if the first
object can be initialized with a structured binding
decomposition of that tuple-like type". More concretely,
1) overload-resolve the initialization as usual
2) if that fails, try the structured binding approach

Semi-obvious questions:
a) can we just do it as a language feature like that..
b) ..or should it be an "opt-in"?
c) how does it interact with class template argument deduction?
Probably quite well, because
pair p = GetAngleVector(2.9);
isn't valid, so the fallback mechanism would eventually kick in, do
structured bindings for the source
and deduce pair<int, double>.

Antonio Perez

unread,
May 15, 2017, 7:45:29 AM5/15/17
to ISO C++ Standard - Future Proposals
Oh! It would interact with normal template argument deduction very nicely. In most cases, the [] brackets could just be replaced with curly braces:

pair p = [2,"Hello world"];
array
<double, 3> arr = [3.7, 9.2, 8.1];
Would be equivalent to:
pair p = {2, "Hello world"};
array<double, 3> arr = {3.7, 9.2, 8.1};

Correspondingly, 
[double, double] GetAngleVector(double x)
{
   
return [cos(x), sin(x)];
}
tuple t = GetAngleVector(2.7);
pair p
= GetAngleVector(2.7);
array
<double, 2> arr = GetAngleVector(2.7);
Would be equivalent to:
template<class T> T GetAngleVector(double x)
{
   
return T{cos(x), sin(x)};
}
tuple t = GetAngleVector<tuple<double, double>>(2.7);
pair p
= GetAngleVector<pair<double, double>>(2.7);
array
<double, 2> arr = GetAngleVector<array<double, 2>>(2.7);

However allowing for reversed structured binding to would mean that the compiler would only have to compile one function, rather than multiple separate functions that differed only in their return type. 
In addition, the structured binding syntax makes it more obvious what exactly's happening. Finally, a structured binding can be directly assigned to another structured binding, so
auto [x,y] = [3,4.7];
WOULD BE valid, whereas
auto [x,y] = {3,4.7};//Currently causes error
Would cause an error (as it currently does). 

Ville Voutilainen

unread,
May 15, 2017, 7:50:43 AM5/15/17
to ISO C++ Standard - Future Proposals
On 15 May 2017 at 14:45, Antonio Perez <ant...@perezexcelsior.com> wrote:
> Oh! It would interact with normal template argument deduction very nicely.
> In most cases, the [] brackets could just be replaced with curly braces:
>
> pair p = [2,"Hello world"];
> array<double, 3> arr = [3.7, 9.2, 8.1];
> Would be equivalent to:
> pair p = {2, "Hello world"};
> array<double, 3> arr = {3.7, 9.2, 8.1};
>
> Correspondingly,
> [double, double] GetAngleVector(double x)
> {
> return [cos(x), sin(x)];
> }
> tuple t = GetAngleVector(2.7);
> pair p = GetAngleVector(2.7);
> array<double, 2> arr = GetAngleVector(2.7);
> Would be equivalent to:
> template<class T> T GetAngleVector(double x)
> {
> return T{cos(x), sin(x)};
> }
> tuple t = GetAngleVector<tuple<double, double>>(2.7);
> pair p = GetAngleVector<pair<double, double>>(2.7);
> array<double, 2> arr = GetAngleVector<array<double, 2>>(2.7);
>
> However allowing for reversed structured binding to would mean that the
> compiler would only have to compile one function, rather than multiple
> separate functions that differed only in their return type.

Huh? You mean that the compiler would use some magic internal type?
The thing I mentioned,
meaning trying structured binding if the initialization isn't
otherwise valid, would mean that
your GetAngleVector would return a tuple. Then we wouldn't need to add
a constructor for
pair, since initializing pair from tuple would fail, and then we try
via structured bindings,
which would then work. No multiple separate functions, just a new way
to convert.

> In addition, the structured binding syntax makes it more obvious what
> exactly's happening. Finally, a structured binding can be directly assigned
> to another structured binding, so
> auto [x,y] = [3,4.7];
> WOULD BE valid, whereas
> auto [x,y] = {3,4.7};//Currently causes error
> Would cause an error (as it currently does).


Well, that approach would seem to introduce a new language-internal type.

Thiago Macieira

unread,
May 15, 2017, 11:56:13 AM5/15/17
to std-pr...@isocpp.org
On segunda-feira, 15 de maio de 2017 04:45:28 PDT Antonio Perez wrote:
> pair p = [2,"Hello world"];
> array<double, 3> arr = [3.7, 9.2, 8.1];
> Would be equivalent to:
> pair p = {2, "Hello world"};
> array<double, 3> arr = {3.7, 9.2, 8.1};

So why do we need another syntax?

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Ville Voutilainen

unread,
May 15, 2017, 12:02:20 PM5/15/17
to ISO C++ Standard - Future Proposals
On 15 May 2017 at 18:56, Thiago Macieira <thi...@macieira.org> wrote:
> On segunda-feira, 15 de maio de 2017 04:45:28 PDT Antonio Perez wrote:
>> pair p = [2,"Hello world"];
>> array<double, 3> arr = [3.7, 9.2, 8.1];
>> Would be equivalent to:
>> pair p = {2, "Hello world"};
>> array<double, 3> arr = {3.7, 9.2, 8.1};
>
> So why do we need another syntax?


Because a new language facility can convert tuples to pairs, pairs to
arrays, tuples to arrays,
arrays to pairs, arrays to tuples and more without those types
defining conversions between
each other.

Thiago Macieira

unread,
May 15, 2017, 1:54:02 PM5/15/17
to std-pr...@isocpp.org
None of that explains why we need the new syntax. Converting from pair to
tuple and vice-versa is a simple library problem, as is converting from arrays
to either. Converting from a homogeneous tuple to an array or an
initializer_list could be a new language feature.

But why do we need square brackets for this?

Vicente J. Botet Escriba

unread,
May 15, 2017, 1:58:53 PM5/15/17
to std-pr...@isocpp.org
Hi,

have you take a look at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0341r0.html?

p0341r0 allows in addition named elements.

<string topSong, person president, double avgTemp> someFactsAboutYear(int year) {
  if(year==1962)
    return {"Stranger On The Shore", Presidents.get("Kennedy"), 14};
}

Vicente

Ville Voutilainen

unread,
May 15, 2017, 2:07:47 PM5/15/17
to ISO C++ Standard - Future Proposals
On 15 May 2017 at 20:53, Thiago Macieira <thi...@macieira.org> wrote:
> On segunda-feira, 15 de maio de 2017 09:02:17 PDT Ville Voutilainen wrote:
>> On 15 May 2017 at 18:56, Thiago Macieira <thi...@macieira.org> wrote:
>> > On segunda-feira, 15 de maio de 2017 04:45:28 PDT Antonio Perez wrote:
>> >> pair p = [2,"Hello world"];
>> >> array<double, 3> arr = [3.7, 9.2, 8.1];
>> >> Would be equivalent to:
>> >> pair p = {2, "Hello world"};
>> >> array<double, 3> arr = {3.7, 9.2, 8.1};
>> >
>> > So why do we need another syntax?
>>
>> Because a new language facility can convert tuples to pairs, pairs to
>> arrays, tuples to arrays,
>> arrays to pairs, arrays to tuples and more without those types
>> defining conversions between
>> each other.
>
> None of that explains why we need the new syntax. Converting from pair to

It might not need new syntax. That remains to be seen. If there's no
new syntax in it,
that means that existing syntaxes grow new capabilities.

> tuple and vice-versa is a simple library problem, as is converting from arrays
> to either. Converting from a homogeneous tuple to an array or an

It doesn't seem to be such a simple problem, considering that the
current library can't do
it and doing it for permutations of multiple types doesn't scale.

Thiago Macieira

unread,
May 15, 2017, 2:32:47 PM5/15/17
to std-pr...@isocpp.org
On segunda-feira, 15 de maio de 2017 11:07:45 PDT Ville Voutilainen wrote:
> On 15 May 2017 at 20:53, Thiago Macieira <thi...@macieira.org> wrote:
> > On segunda-feira, 15 de maio de 2017 09:02:17 PDT Ville Voutilainen wrote:
> >> On 15 May 2017 at 18:56, Thiago Macieira <thi...@macieira.org> wrote:
> >> > On segunda-feira, 15 de maio de 2017 04:45:28 PDT Antonio Perez wrote:
> >> >> pair p = [2,"Hello world"];
> >> >> array<double, 3> arr = [3.7, 9.2, 8.1];
> >> >> Would be equivalent to:
> >> >> pair p = {2, "Hello world"};
> >> >> array<double, 3> arr = {3.7, 9.2, 8.1};
> >> >
> >> > So why do we need another syntax?
> >>
> >> Because a new language facility can convert tuples to pairs, pairs to
> >> arrays, tuples to arrays,
> >> arrays to pairs, arrays to tuples and more without those types
> >> defining conversions between
> >> each other.
> >
> > None of that explains why we need the new syntax. Converting from pair to
>
> It might not need new syntax. That remains to be seen. If there's no
> new syntax in it,
> that means that existing syntaxes grow new capabilities.

That's my point. I don't see any new capability above, hence no need for any
new syntax. The new features you discussed are about variables already
initialised.

> > tuple and vice-versa is a simple library problem, as is converting from
> > arrays to either. Converting from a homogeneous tuple to an array or an
>
> It doesn't seem to be such a simple problem, considering that the
> current library can't do
> it and doing it for permutations of multiple types doesn't scale.

Sorry, I didn't follow this.

It should be trivial to add a constructor to std::pair that takes std::tuple
with the same parameters. It should be easy to add a std::tuple constructor
that takes std::pair of the same types (provided there are exactly two of
them).

The new language feature would be to initialise an aggregate with std::tuple
and vice-versa. Given that structured bindings offers std::tuple_size and
such, we need a language feature that returns the number of elements in that
aggregate, their types and allows access to the values in a given object. We
could even call this feature "reflection".

Ville Voutilainen

unread,
May 15, 2017, 3:13:18 PM5/15/17
to ISO C++ Standard - Future Proposals
On 15 May 2017 at 21:32, Thiago Macieira <thi...@macieira.org> wrote:
>> > tuple and vice-versa is a simple library problem, as is converting from
>> > arrays to either. Converting from a homogeneous tuple to an array or an
>>
>> It doesn't seem to be such a simple problem, considering that the
>> current library can't do
>> it and doing it for permutations of multiple types doesn't scale.
>
> Sorry, I didn't follow this.
>
> It should be trivial to add a constructor to std::pair that takes std::tuple
> with the same parameters. It should be easy to add a std::tuple constructor

Good luck with that trivial exercise. Now, do it again and add a
constructor to std::pair
that takes an array. Repeat the exercise for tuple. And array. Now go
add such things
to optional and variant while you're at it.

Once you've tried, you may come away thinking that perhaps a language extension
would work better.

> The new language feature would be to initialise an aggregate with std::tuple
> and vice-versa. Given that structured bindings offers std::tuple_size and
> such, we need a language feature that returns the number of elements in that
> aggregate, their types and allows access to the values in a given object. We
> could even call this feature "reflection".

Mere reflection doesn't make it work for all cases. So we need
something more uniform than that.

HarD Gamer

unread,
May 15, 2017, 4:27:17 PM5/15/17
to ISO C++ Standard - Future Proposals

inkwizyt...@gmail.com

unread,
May 15, 2017, 4:56:00 PM5/15/17
to ISO C++ Standard - Future Proposals
IIRC having structural binding allow to have "reverse binding" and poor man reflection.

You can easy create around 20 functions that bind from 1 to 20 values to tuple from any type that support structural binding.
This is only weak part of this solution but you can codegen this to some number that is reasonable.

With that you can you can construct any type you want using one variable template that get that tuple and final type.

Vicente J. Botet Escriba

unread,
May 15, 2017, 5:41:22 PM5/15/17
to std-pr...@isocpp.org
You need just to add a constructor from any ProductType with the same
elements types ;-)
> The new language feature would be to initialise an aggregate with std::tuple
> and vice-versa.
p0341r0 associates the type <T1, ..., Tn> to {t1, ..., tn}.
I believe this is quite useful. I believe it is unfortunate that the
expression {t1, ..., tn} has not type currently.

<T1, ..., Tn> should of course be a product type, and tuple should be
constructible from any product type, in particular from <T1, ..., Tn>.

> Given that structured bindings offers std::tuple_size and
> such, we need a language feature that returns the number of elements in that
> aggregate, their types and allows access to the values in a given object. We
> could even call this feature "reflection".
>
See p0341r0. I don't know who to provide the access to any type
supporting by Structured binding using Reflection. There is
customization case that cannot be reflected. This the main reason of p327r1.

I've requested in the Reflection ML help on this, who the reflection
interface could help to reflect structured binding types, but only the
easy case for structs was defined.

Vicente

Vicente J. Botet Escriba

unread,
May 15, 2017, 5:44:50 PM5/15/17
to std-pr...@isocpp.org
An unnamed Product type (the members have no special meaning) should be
convertible from another ProductType with the same element types (or
even from convertibles to). However when the members have a name, this
is different.

I don't want a struct Date class to be convertible [int, int, int], at
least not implicitly.

As P0327R1 shows that defining constructors that take ProductTypes
instead of specific types (as tuple) simplifies a lot the number of
needed constructors., but each class should be able to define if these
constructors are desired and if they are explicit, or implicit.

Vicente

Vicente J. Botet Escriba

unread,
May 15, 2017, 5:53:16 PM5/15/17
to std-pr...@isocpp.org
Le 15/05/2017 à 11:49, Antonio Perez a écrit :
Structured binding is an extremely useful concept because it allows the efficent and intuitive unpacking of data-structures into their individual values, and it works on a wide range of data structures. Not only does it work on tuples and pairs, but it also works on structs defined by the programmer without any additional effort on their part. I propose making this language feature more complete by allowing structured binding to take place in reverse:

tuple a = {1, 3.7}; //Creates a tuple of type tuple<int, double> with values 1 and 3.7
tuple b
= [1, 3.7]; //Ditto

 Where is this useful?
The above example isn't particularly useful, HOWEVER here's a more real usage case:

[double, double] GetAngleVector(double x)
{
   
return [cos(x), sin(x)];
}

//All of these would be 100% valid:
tuple t
= GetAngleVector(2.7);
pair p
= GetAngleVector(2.7);
array
<double, 2> arr = GetAngleVector(2.7);
auto [x,y] = GetAngleVector(2.7);

struct Point2D {
   
double x,y;
};
Point2D point = GetAngleVector(2.7);

Here, the function returns a structured-binding that will automatically map to whatever it's assigned to without issue.
Are you saying that any type supporting structured binding will be constructible from [E1, ..., En] where Ek are the types of its elements.
I could understand the opposite conversion as [E1, ..., En] doesn't give any meaning to Ek other than the position. The types you use in the example share almost all this feature so I understand the conversion, but Point2D is giving a meaning to the first and second elements. The first is the ordinate and the second the coordinate. Would you suggest that given

    struct Point2DR {
       
double y,x;
    };

The following will be valid as well?

    Point2DR point = GetAngleVector(2.7);

And

    Point2DR point = Point2D{0.5, 1.2};

I don't want these implicit constructions.


Something similar could be achieved in standard C++17, although it's unseemly and difficult to read:
tuple<double, double> GetAngleVector(double x)
{
   
return {cos(x), sin(x)};
}

//All of these would be 100% valid:
pair p
= std::make_from_tuple<pair<double, double>, tuple<double,double>>(GetAngleVector(2.7));
array
<double, 2> arr = std::make_from_tuple<std::array<double, 2>, tuple<double,double>>(GetAngleVector(2.7));

struct Point2D {
   
double x,y;
};
Point2D point =
std::make_from_tuple<Point2D, tuple<double,double>>(GetAngleVector(2.7))


You don't need to give the second parameter to make_from_tuple.

With the P0327R1 you could have

tuple<double, double> GetAngleVector(double x)
{
   
return {cos(x), sin(x)};
}

pair p = product_type::make_from<pair>(GetAngleVector(2.7));
array
<double, 2> arr = product_type::make_from<array_tc>(GetAngleVector(2.7));

struct Point2D {
   
double x,y;
};
Point2D point =
product_type::make_from<Point2D>(GetAngleVector(2.7))


if product_type::make_from uses T{t1, ..., tn} instead T(t1, ..., tn). (This is not the case today), but we could define a product_type::make_from2 that has this semantics.
 

Note that the construction is explicit in this case.


Vicente

Vicente J. Botet Escriba

unread,
May 15, 2017, 6:12:07 PM5/15/17
to std-pr...@isocpp.org
Le 15/05/2017 à 22:27, HarD Gamer a écrit :
I'll repeat my comment on this thread, in case you want to say what do you think of P0341?.

Maybe you will be interested in

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0341r0.html

In particular the pack types and the named pack types are very similar
to what you are proposing in your paper ;-)

Vicente
Reply all
Reply to author
Forward
0 new messages