(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).
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.
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.
struct RealName
{
RealName(const std::tuple<int, double> &);
int a_member;
double other_member;
};
RealName s = std::make_tuple(1, 2.3);
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.
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;
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
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 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.
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();
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.
auto parts = scavage_junkyard();
parts.engine;
auto [junk_engine, junk_dashboard, junk_stickshift] = scavage_junkyard();