Idea: Extension of structured bindings to definition of structs

198 views
Skip to first unread message

Eyal Rozenberg

unread,
Aug 26, 2017, 4:17:14 PM8/26/17
to std-pr...@isocpp.org
(This was mis-posted earlier to std-discussion, sorry.)

----

Today ( = C++17 standard draft, with section [dcl.struct.bind] in
particular), we can define multiple variables using, say:

auto [ x, y ] = f();

where f() returns an array, or a tuple, or a struct with two members.
That's very nice and good.

Now, what if I wanted my new variables x and y to be members of a
struct? At the moment, I can only define a struct using types I can
specify in advance, e.g.

struct { int x; double y; } s;

and together with initialization, this becomes:

struct { int x; double y; } s = std::make_tuple(1, 2.3);

what I am imagining, or suggesting, is being able to write:

struct { auto x; auto y; } s = std::make_tuple(1, 2.3);

which is interpreted as though we've written

struct {
decltype(1) x;
decltype(2.3) y;
} s = std::make_tuple(1, 2.3);

with the possible initializers being anything that can be used as the
initializer for structured binding of x and y together as stand-alone
variables.

Motivation
-----------

Well, first there's the obvious Don't Repeat Yourself even in my simple
example.

Additional motivation is to be found if we consider why we have auto
variables in the first place, since C++11. It's good to be able to say

auto x { long_expression_with_difficult_type_to_figure_out };

rather than having to determine the type, or repeating yourself twice with

decltype(expression_with_difficult_type_to_figure_out) x {
expression_with_difficult_type_to_figure_out
};

Unfortunately, at the moment we lose this feature if x needs to be a
member of a struct - even if we know the type of the other members. I
can't say

struct {
decltype(a_2_tuple_with_difficult_first_type_and_double) x;
double y;
} s { a_2_tuple_with_difficult_first_type_and_double };

and my idea, or proposition, would make this possible.

This is also "better" than structured bindings in the sense that it lets
you convert a product type with unidentified components to one whose
components are identified, e.g.:

template<size_t N> std::array<widget_t, N> generate_widgets();
// ...
struct special_widgets {
auto foo; auto bar; auto baz;
} s = generate_widgets<3>();



Estimated Impact
----------------

I'm not a C++ language lawyer, but I think that this kind of syntax
should not clash with anything already existing in the language.

This is syntactic sugar, it seems, so it should not affect the
expressibility of C++.

This feature is close enough to what structured binding does already, so
that the burden on compiler authors to implement this would be very
limited (in my uninformed opinion).


Additional ideas if you want to get super wild
-----------------------------------------------

1. Allow multiple members of the same type like in usual struct
declarations, i.e.

struct { auto x, y; auto z; } s = std::make_tuple(1, 2, 3.4);

2. Allow only having some members be auto, i.e.

struct { auto x; double y; } s = std::make_tuple(1, 2.3);

3. Allow all-auto structs

auto struct { x, y } s = std::make_tuple(1, 2.3);

or perhaps

auto struct [ x, y ] s = std::make_tuple(1, 2.3);

if you like square brackets better.


Nicol Bolas

unread,
Aug 26, 2017, 9:08:11 PM8/26/17
to ISO C++ Standard - Future Proposals
On Saturday, August 26, 2017 at 4:17:14 PM UTC-4, Eyal Rozenberg wrote:
(This was mis-posted earlier to std-discussion, sorry.)

----

Today ( = C++17 standard draft, with section [dcl.struct.bind] in
particular), we can define multiple variables using, say:

   auto [ x, y ] = f();

where f() returns an array, or a tuple, or a struct with two members.
That's very nice and good.

Now, what if I wanted my new variables x and y to be members of a
struct?

Stop.

They already are "members of a struct". Whatever `f()` returned is either a class type with all of its members being publicly accessible or it's a class type that has tuple-access machinery (or it's an array). So for all useful purposes, it's already a member of a struct.

So why do you want to decompose an object, just to recompose it as a different object? And why do you want to do this to an unnamed struct? What practical purpose are you trying to achieve here?

At the moment, I can only define a struct using types I can
specify in advance, e.g.

   struct { int x; double y; } s;

and together with initialization, this becomes:

   struct { int x; double y; } s = std::make_tuple(1, 2.3);

what I am imagining, or suggesting, is being able to write:

   struct { auto x; auto y; } s = std::make_tuple(1, 2.3);

I have a tuple. Why would I not just use the tuple?

which is interpreted as though we've written

   struct {
     decltype(1)   x;
     decltype(2.3) y;
   } s = std::make_tuple(1, 2.3);

with the possible initializers being anything that can be used as the
initializer for structured binding of x and y together as stand-alone
variables.

Motivation
-----------

Well, first there's the obvious Don't Repeat Yourself even in my simple
example.
Additional motivation is to be found if we consider why we have auto
variables in the first place, since C++11.

Avoiding repetition of `decltype` expressions is not why we have `auto`.

It's good to be able to say

   auto x { long_expression_with_difficult_type_to_figure_out };

rather than having to determine the type, or repeating yourself twice with

   decltype(expression_with_difficult_type_to_figure_out) x {
     expression_with_difficult_type_to_figure_out
   };
Unfortunately, at the moment we lose this feature if x needs to be a
member of a struct - even if we know the type of the other members. I
can't say

   struct {
     decltype(a_2_tuple_with_difficult_first_type_and_double) x;
     double y;
   } s { a_2_tuple_with_difficult_first_type_and_double };

Well, yes. Because a type has to make sense outside of the initialization of some object of that type. Variables don't.

What you want requires that the compiler delay figuring out what is going on within the struct definition until after some variable of that type is initialized.

and my idea, or proposition, would make this possible.

This is also "better" than structured bindings in the sense that it lets
you convert a product type with unidentified components to one whose
components are identified, e.g.:

   template<size_t N> std::array<widget_t, N> generate_widgets();
   // ...
   struct special_widgets {
      auto foo; auto bar; auto baz;
   } s = generate_widgets<3>();



Estimated Impact
----------------

I'm not a C++ language lawyer, but I think that this kind of syntax
should not clash with anything already existing in the language.

This is syntactic sugar, it seems, so it should not affect the
expressibility of C++.

This feature is close enough to what structured binding does already, so
that the burden on compiler authors to implement this would be very
limited (in my uninformed opinion).

But it is in no way like what structured binding does. What you want declares a new type; structured binding doesn't declare types. Structured binding declares a hidden variable and a number of names. But it doesn't declare a type.

Thiago Macieira

unread,
Aug 27, 2017, 12:11:13 AM8/27/17
to std-pr...@isocpp.org
On Saturday, 26 August 2017 18:08:11 PDT Nicol Bolas wrote:
> I have a tuple. Why would I not just *use the tuple*?

Because tuples are horrible. From
https://www.kdab.com/tuple-pair-cpp-apis/

"Quick: When you design C++ APIs, when and how should you use pair and tuple?
The answer is as simple as it is surprising: Never. Ever."

And yet some people will. So when interacting with those API, you need to
"correct" it with proper names.

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

Nevin Liber

unread,
Aug 27, 2017, 1:21:29 AM8/27/17
to std-pr...@isocpp.org
On Sat, Aug 26, 2017 at 11:11 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Saturday, 26 August 2017 18:08:11 PDT Nicol Bolas wrote:
> I have a tuple. Why would I not just *use the tuple*?

Because tuples are horrible. From
        https://www.kdab.com/tuple-pair-cpp-apis/

"Quick: When you design C++ APIs, when and how should you use pair and tuple?
The answer is as simple as it is surprising: Never. Ever."

And yet some people will. So when interacting with those API, you need to
"correct" it with proper names.

Prior to C++17 I'd agree, but we haven't have structured bindings in the language long enough to know how big an issue it still is.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Nicol Bolas

unread,
Aug 27, 2017, 10:11:04 AM8/27/17
to ISO C++ Standard - Future Proposals
On Sunday, August 27, 2017 at 12:11:13 AM UTC-4, Thiago Macieira wrote:
On Saturday, 26 August 2017 18:08:11 PDT Nicol Bolas wrote:
> I have a tuple. Why would I not just *use the tuple*?

Because tuples are horrible. From
        https://www.kdab.com/tuple-pair-cpp-apis/

"Quick: When you design C++ APIs, when and how should you use pair and tuple?
The answer is as simple as it is surprising: Never. Ever."

And yet some people will. So when interacting with those API, you need to
"correct" it with proper names.

OK, the problem with tuples in APIs, as outlined in that post, is that they lack any semantic information. They have no meaningful variable names, and they have no meaningful type names.

If I'm using an API that uses tuples to the extent that it becomes difficult to understand the code when actually using a tuple, why would I be using an unnamed struct to solve this? Would it not make more sense to do this:

struct RealName
{
 
RealName(const std::tuple<int, double> &);

 
int a_member;
 
double other_member;
};

And then in the actual function:

RealName s = std::make_tuple(1, 2.3);

This improves code reuse, since `RealName` can be used in multiple places. It improves readability, since `RealName` would convey semantic information about the collection of values that `decltype(s)` cannot. And so forth. The trivial repetition of typing the type names in two (or even three) places in `RealName` will be meaningless next to how often `RealName` is used.

Presumably, the reason I'm not using structured binding is because I want to pass the collection `s` around to other people, yes? Otherwise, you would just use structured binding to provide the semantic info. Given that this is the case, we need a proper name so that we understand what this aggregate actually means.

If this is a one-off case, where you're only calling one function that has a `tuple` API in one place... why are you creating an object for the values at all? If it's a one-off, use structured binding, or just tough-through the `get<>` interface for that one function.

What circumstances would I be in where I would be writing an inline struct to convert this object? If it happens frequently enough to obfuscate my code, the best solution is a named struct. If it's not that frequent, just use structured binding or the actual return value.

Eyal Rozenberg

unread,
Aug 30, 2017, 3:59:43 AM8/30/17
to std-pr...@isocpp.org
You would be using an unnamed struct for the same reason you use an
"unnamed type" for non-struct variables with auto. We write:

auto x = expr;

although we could write

using whatever_t = decltype(expr);

// ...

whatever_t x = expr;

except we do it for several values which are grouped together in a
struct. The second option does the exact same thing, but it's annoying
and verbose.


On 27/08/17 16:11, Nicol Bolas wrote:
> difficult to understand the code when actually using a tuple, why would
> I be using an /unnamed/ struct to solve this? Would it not make more
> sense to do this:
>
> |
> structRealName
> {
> RealName(conststd::tuple<int,double>&);
>
> inta_member;
> doubleother_member;
> };
> |
>
> And then in the actual function:
>
> |
> RealNames =std::make_tuple(1,2.3);
> |
>
> This improves code reuse, since `RealName` can be used in multiple
> places. It improves readability, since `RealName` would convey semantic
> information about the collection of values that `decltype(s)` cannot.
> And so forth. The trivial repetition of typing the type names in two (or
> even three) places in `RealName` will be meaningless next to how often
> `RealName` is used.
>
> Presumably, the reason I'm /not/ using structured binding is because I
> want to pass the collection `s` around to other people, yes? Otherwise,
> you would just use structured binding to provide the semantic info.
> Given that this is the case, we need a proper name so that we understand
> what this aggregate actually means.
>
> If this is a one-off case, where you're only calling one function that
> has a `tuple` API in one place... why are you creating an object for the
> values at all? If it's a one-off, use structured binding, or just
> tough-through the `get<>` interface for that one function.
>
> What circumstances would I be in where I would be writing an /inline
> struct/ to convert this object? If it happens frequently enough to
> obfuscate my code, the best solution is a named struct. If it's not that
> frequent, just use structured binding or the actual return value.
>
> --
> 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
> <mailto:std-proposal...@isocpp.org>.
> To post to this group, send email to std-pr...@isocpp.org
> <mailto:std-pr...@isocpp.org>.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ff2ceb41-caca-4659-9a7d-4da9c2046810%40isocpp.org
> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ff2ceb41-caca-4659-9a7d-4da9c2046810%40isocpp.org?utm_medium=email&utm_source=footer>.

Nicol Bolas

unread,
Aug 30, 2017, 11:02:27 AM8/30/17
to ISO C++ Standard - Future Proposals
On Wednesday, August 30, 2017 at 3:59:43 AM UTC-4, Eyal Rozenberg wrote:
You would be using an unnamed struct for the same reason you use an
"unnamed type" for non-struct variables with auto. We write:

  auto x = expr;

although we could write

  using whatever_t = decltype(expr);

  // ...

  whatever_t x = expr;

except we do it for several values which are grouped together in a
struct.

But the reason I'm using `auto` is because I either:

1) Don't need to write the name of the type, since it is obvious by inspection
2) Do not want to write the name of the type, since it's long, complex, etc

Why do I need to create a type that contains a bunch of such variables? And why would I need to do it inline like this?

If I'm creating a collection of values, then that collection generally has some meaning to the program. There are probably several functions that take it as parameters, or there are member functions that operate on it, or whatever.

So what is the point of having this unnamed struct that uses automatically-deduced values? Who am I going to pass it to? What operations am I going to perform on it? Why did I make a struct out of it instead of just having a number of loose variables?

Nevin Liber

unread,
Aug 30, 2017, 11:17:26 AM8/30/17
to std-pr...@isocpp.org
On Wed, Aug 30, 2017 at 2:59 AM, Eyal Rozenberg <eya...@technion.ac.il> wrote:
You would be using an unnamed struct for the same reason you use an
"unnamed type" for non-struct variables with auto. We write:

  auto x = expr;

Even though you don't spell it out, that type already has a name.  auto doesn't create a new type.

You are proposing yet another syntax to create a new type.  As for its use, about the only thing you can do with it is pass it to a template function (or equivalent).  That use seems fairly marginal.  You need much better usage examples for motivation.

the.ultim...@gmail.com

unread,
Sep 1, 2017, 3:21:48 AM9/1/17
to ISO C++ Standard - Future Proposals


Le mercredi 30 août 2017 17:02:27 UTC+2, Nicol Bolas a écrit :
But the reason I'm using `auto` is because I either:

1) Don't need to write the name of the type, since it is obvious by inspection
2) Do not want to write the name of the type, since it's long, complex, etc


3) Cannot write the name of the type, since it's unutterable (like the type of a lambda)

the.ultim...@gmail.com

unread,
Sep 1, 2017, 3:27:03 AM9/1/17
to ISO C++ Standard - Future Proposals


Le mercredi 30 août 2017 09:59:43 UTC+2, Eyal Rozenberg a écrit :
 We write:

  auto x = expr;

although we could write

  using whatever_t = decltype(expr);

  // ...

  whatever_t x = expr;

except we do it for several values which are grouped together in a
struct. The second option does the exact same thing, but it's annoying
and verbose.


Maybe I don't understand auto and decltype enough, but in the first case :

int a = 5;
int& b = a;
auto c = b;

c has type int. In the second case:

int a = 5;
int& b = a;
using whatever_t = decltype(b);
whatever_t c = b;

c has type int&.

I may be wrong, but it does not "do the exact same thing" to me.

Larry Evans

unread,
Sep 2, 2017, 4:35:24 AM9/2/17
to std-pr...@isocpp.org
On 08/30/2017 10:16 AM, Nevin Liber wrote:
> On Wed, Aug 30, 2017 at 2:59 AM, Eyal Rozenberg <eya...@technion.ac.il>
> wrote:
>
>> You would be using an unnamed struct for the same reason you use an
>> "unnamed type" for non-struct variables with auto. We write:
>>
>> auto x = expr;
>>
>
> Even though you don't spell it out, that type already has a name. auto
> doesn't create a new type.
>
> You are proposing yet another syntax to create a new type. As for its use,
> about the only thing you can do with it is pass it to a template function
> (or equivalent). That use seems fairly marginal. You need much better
> usage examples for motivation.
>
Boost spirit has a macro, BOOST_FUSION_ADAPT_STRUCT, which maps a tuple
to a struct, as explained here:

http://ciere.com/cppnow15/x3_docs/spirit/tutorials/employee___parsing_into_structs.html

What the OP is proposing *seems* to obviate the need for
BOOST_FUSION_ADAPT_STRUCT.

That's one real world motivation example, IIUC.

-regards,
Larry


Nicol Bolas

unread,
Sep 2, 2017, 10:12:05 AM9/2/17
to ISO C++ Standard - Future Proposals, cpplj...@suddenlink.net
P0648 would do so far more effectively. And far more directly, without having to create a new type.

Eyal Rozenberg

unread,
Sep 4, 2017, 10:16:47 AM9/4/17
to std-pr...@isocpp.org, Nevin Liber
I'm not actually suggesting a new way to create a type. The type is
created anyway if I write:

struct {
decltype(std::get<0>(get_car_parts())) engine;
decltype(std::get<0>(get_car_parts())) dashboard;
decltype(std::get<0>(get_car_parts())) stickshift;
} car_parts = get_car_parts();

I'm just suggesting some syntactic sugar for that statement:

struct { auto engine, dashboard, stickshift; } car_parts =
get_car_parts();

A third alternative is not to create a type and write:

auto [ car_engine, car_dashboard, car_stickshift ] = get_car_parts();

but I (we?) should frown upon this use of prefixes in identifiers.

As Larry Evans suggest, this scenario occasionally happens, so we're not
talking about an esoteric use-case.

Eyal
> <mailto:ne...@eviloverlord.com>>  +1-847-691-1404
>
> --
> 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
> <mailto:std-proposal...@isocpp.org>.
> To post to this group, send email to std-pr...@isocpp.org
> <mailto:std-pr...@isocpp.org>.
> To view this discussion on the web visit
> https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMPQTnqdWLQ1UV8jF173jDXYQQx-abwwNV3v3iknocJ%3DQ%40mail.gmail.com
> <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMPQTnqdWLQ1UV8jF173jDXYQQx-abwwNV3v3iknocJ%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Eyal Rozenberg

unread,
Sep 4, 2017, 12:57:48 PM9/4/17
to Nicol Bolas, std-pr...@isocpp.org


On 8/30/17 5:02 PM, Nicol Bolas wrote:
> But the reason I'm using `auto` is because I either:
>
> 1) Don't need to write the name of the type, since it is obvious by
> inspection
> 2) Do not want to write the name of the type, since it's long, complex, etc
>
> Why do I need to create a /type/ that contains a bunch of such
> variables? And why would I need to do it /inline/ like this?
> ...


See my email to Nevin Liber from a couple of minutes ago. We are
_already_ creating the type in this case (at least in one of the
alternatives).

My - I would say tiny - suggestion is to add a bit more syntactic sugar
for facilitating that, in the same vein as auto declaration of multiple
non-struct variables. It makes coding the thing more fluent,.


Fluency, DRY. Also, apparently, replacing a Boost Spirit X3 macro which
does this for us :-)
http://ciere.com/cppnow15/x3_docs/spirit/tutorials/employee___parsing_into_structs.html



> Why did I make a struct out of it instead of just having a number of
loose variables? ... what is the point of having this unnamed struct
that uses automatically-deduced values? Who am I going to pass it to?
What operations am I going to perform on it?

No, you aren't - and that _is_ the whole point! If you had wanted a
carefully-defined type with methods and such to pass around and expose,
you would have gone to the trouble of defining it properly, and with a
_name_.

Instead, you just fluently create the struct, with an anonymous
"throw-away" POD type, without you having to mentally bother with the
definition. This is actually reason (1.) you gave for using auto... You

"don't need to write the name of the type, since it is obvious by
inspection."

and that's what I want too.

Eyal

Nicol Bolas

unread,
Sep 4, 2017, 1:21:01 PM9/4/17
to ISO C++ Standard - Future Proposals, jmck...@gmail.com
On Monday, September 4, 2017 at 12:57:48 PM UTC-4, Eyal Rozenberg wrote:
On 8/30/17 5:02 PM, Nicol Bolas wrote:
> But the reason I'm using `auto` is because I either:
>
> 1) Don't need to write the name of the type, since it is obvious by
> inspection
> 2) Do not want to write the name of the type, since it's long, complex, etc
>
> Why do I need to create a /type/ that contains a bunch of such
> variables? And why would I need to do it /inline/ like this?
 > ...

See my email to Nevin Liber from a couple of minutes ago. We are
_already_ creating the type in this case (at least in one of the
alternatives).

Who is "we" who is "already creating the type"? My question was why do you need to create types in this fashion? I for one have never felt the need to create an inline type in the manner you describe.

You have yet to provide justification for why you need to have a type here instead of just loose variables.

My - I would say tiny - suggestion is to add a bit more syntactic sugar
for facilitating that, in the same vein as auto declaration of multiple
non-struct variables. It makes coding the thing more fluent,.

Fluent for whom? For what purpose?

Provide an actual, genuine example of real-world code where you need to create such a type inline. Not just a place where it could be used, but an example of where doing so is necessary for doing what you need to do.

Fluency, DRY. Also, apparently, replacing a Boost Spirit X3 macro which
does this for us :-)
http://ciere.com/cppnow15/x3_docs/spirit/tutorials/employee___parsing_into_structs.html

No, it doesn't. That struct already exists; the macro doesn't define it in that location. It simply builds tuple decomposition machinery for the existing type.

Again, P0648 would obsolete that macro; your proposal would not. Your proposal is about generating types; I don't know how you keep confusing that with decomposition.

 > Why did I make a struct out of it instead of just having a number of
loose variables? ... what is the point of having this unnamed struct
that uses automatically-deduced values? Who am I going to pass it to?
What operations am I going to perform on it?

No, you aren't - and that _is_ the whole point! If you had wanted a
carefully-defined type with methods and such to pass around and expose,
you would have gone to the trouble of defining it properly, and with a
_name_.

Instead, you just fluently create the struct, with an anonymous
"throw-away" POD type, without you having to mentally bother with the
definition.

That doesn't explain why it is important that you create this "throw-away POD type".

This is actually reason (1.) you gave for using auto... You

"don't need to write the name of the type, since it is obvious by
inspection."
 
and that's what I want too.

`auto` deduces a type. You want to create a type. There's a fundamental difference there.

Nevin Liber

unread,
Sep 4, 2017, 3:05:28 PM9/4/17
to Eyal Rozenberg, std-pr...@isocpp.org
On Mon, Sep 4, 2017 at 9:16 AM, Eyal Rozenberg <eya...@technion.ac.il> wrote:
I'm not actually suggesting a new way to create a type. The type is created anyway if I write:

  struct {
      decltype(std::get<0>(get_car_parts())) engine;
      decltype(std::get<0>(get_car_parts())) dashboard;
      decltype(std::get<0>(get_car_parts())) stickshift;
  } car_parts = get_car_parts();

Syntactic sugar or not, what can you do with an object of that type?  You can either:
  • Pass it to a templated function
  • Capture it in a lambda
As I stated before, that usage seems fairly marginal and not worth a language feature.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

Eyal Rozenberg

unread,
Sep 4, 2017, 6:00:50 PM9/4/17
to Nevin Liber, std-pr...@isocpp.org
Please read my answer to Nicol Bolas.

To reiterate briefly:

I don't want to do any of those things. I would use this struct's
members, and they're in a struct to indicate that they're related and to
name them together.

More importantly, I would have the person reading my code go "Oh, so
he's using parts of the car" rather than have to to keep track of the
relation of the loose variables in his/her mind.

(This especially true if my function was named scavage_junkyard() rather
than get_car_parts() ).

Eyal

On 9/4/17 9:04 PM, Nevin Liber wrote:
> On Mon, Sep 4, 2017 at 9:16 AM, Eyal Rozenberg <eya...@technion.ac.il
> <mailto:eya...@technion.ac.il>> wrote:
>
> I'm not actually suggesting a new way to create a type. The type is
> created anyway if I write:
>
>   struct {
>       decltype(std::get<0>(get_car_parts())) engine;
>       decltype(std::get<0>(get_car_parts())) dashboard;
>       decltype(std::get<0>(get_car_parts())) stickshift;
>   } car_parts = get_car_parts();
>
>
> Syntactic sugar or not, what can you do with an object of that type?
> You can either:
>
> * Pass it to a templated function
> * Capture it in a lambda
>
> As I stated before, that usage seems fairly marginal and not worth a
> language feature.
> --
>  Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com
> <mailto:ne...@eviloverlord.com>>  +1-847-691-1404

Nicol Bolas

unread,
Sep 4, 2017, 6:59:02 PM9/4/17
to ISO C++ Standard - Future Proposals, ne...@eviloverlord.com
On Monday, September 4, 2017 at 6:00:50 PM UTC-4, Eyal Rozenberg wrote:
Please read my answer to Nicol Bolas.

To reiterate briefly:

I don't want to do any of those things. I would use this struct's
members, and they're in a struct to indicate that they're related and to
name them together.

That's not a good enough reason to create a type. Creating such a type changes precisely nothing about the behavior of your code (save for making it less efficient, since real structured binding could be cheaper than the copy/move you're currently required to do). And if you don't use it as a type by passing it to someone, capturing in a lambda, or whatever, then there is no objective utility to do so.

It's just personal preference. And that's not good enough for a language feature.

And quite frankly, if there is so much distance between a variable and its use that you're not sure what that variable is related to anymore, then perhaps you need better variable names or shorter functions.

More importantly, I would have the person reading my code go "Oh, so
he's using parts of the car" rather than have to to keep track of the
relation of the loose variables in his/her mind.

If it's important to you that user's know that you're "using parts of the car", why is the "parts of the car" aggregate not so named? If `scavage_junkyard` returns "car_parts", why would you not simply do:

auto parts = scavage_junkyard();
parts
.engine;

The use case you're talking about here is one where you need to decompose the type returned. Well, if the type is already meaningful... why do you need to decompose it? Why would `scavage_junkyard` return a `tuple` or something opaque instead of a `car_parts`?

Similarly, why is this not appropriate:

auto [junk_engine, junk_dashboard, junk_stickshift] = scavage_junkyard();

This is perfectly functional. The names tell you that they're from the junkyard, and which part each comes from.

Indeed, the whole point of structured binding (to me) is to deal with cases where there is no meaningful connection between the return values of a function. Like `map.insert` or whatever, which returns an opaque `pair`. Or `to_chars`, which returns two values that don't really mean much together.

Please provide a genuine use case, not a hypothetical.
Reply all
Reply to author
Forward
0 new messages