A plea to reconsider adding Structured Bindings to language

644 views
Skip to first unread message

Marc Mutz

unread,
Oct 5, 2016, 6:36:35 AM10/5/16
to hsu...@microsoft.com, bja...@stroustrup.com, g...@microsoft.com, std-pr...@isocpp.org
Hi Herb, Bjarne, Gabriel,

I heard about Structured Bindings for the first time at CppCon this year, and
I'd like to raise my concerns, esp. since half of the panel was crazy about
them.

I strongly believe that the premise that returning std::tuple from functions
to solve the multiple-return-types problem is good practice, is fundamentally
wrong.

It is not good in any sense of the word. It is horrible. Indeed, it is so
horrible that you felt inclined to add a new language feature just to make it
bearable.

The reaons it's horrible, is because it reverses the C++ principle that the
implementor of a library should have to do the work, not the user.

It reverses the principle by requiring the *caller* to choose the names of the
values returned, when it should be the implementor of the function who chooses
the names.

In conjunction with auto deduction, the caller choosing the names means that
the code becomes brittle in the face of changes to the return type, e.g. when
reordering fields to fill padding holes.

Structured Bindings would be acceptable if, like scripting languages, we
didn't have anything else to work with.

But we do: We can return a struct.

IMHO, the correct solution to the multiple return value problem is to return a
struct with properly named fields, not a pair and not a tuple.

I fear Structured Bindings will lead to an explosion of *really* bad API that
returns std::pair or std::tuple when it should return a small struct with
well-named data members instead, because a) the implementor couldn't be
bothered to pick good names for a return struct, and b) tuples can be defined
on the fly, in the function declaration, whereas structs can not.

I believe there's a proposal for allowing to define structs in function
declarations already, but I failed to find it.

Consider:

std::map::insert(...) -> std::pair<iterator, bool>

or the other way around, I can never remember, which is a problem that
structured bindings don't solve for me. Yes, in this case using the iterator
as a boolean will fail, but what about algorithms that return multiple
iterators? If you get the order wrong, then you have a bug.

No, map::insert() should have returned a

struct { iterator iterator; bool inserted; };

so one could say:

if (map.insert(...).inserted)

or

auto result = map.insert(...)
if (result.inserted)

To summarise: I feel that Structured Bindings are too easy to use incorrectly,
because they put the burden of choosing a name for the fields of a return
value on the caller, instead of the implementor, of the function. I'd like to
see the pattern to return structs from functions strengthened, not the anti-
pattern of returning pairs and tuples.

I therefore hope that I can persuade you to reconsider adding Structured
Bindings to C++17.

Thanks,
Marc

--
Marc Mutz <marc...@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - Qt, C++ and OpenGL Experts

Ville Voutilainen

unread,
Oct 5, 2016, 6:40:39 AM10/5/16
to ISO C++ Standard - Future Proposals, Herb Sutter, Bjarne Stroustrup, Gabriel Dos Reis
On 5 October 2016 at 13:38, Marc Mutz <marc...@kdab.com> wrote:
> In conjunction with auto deduction, the caller choosing the names means that
> the code becomes brittle in the face of changes to the return type, e.g. when
> reordering fields to fill padding holes.

There have been discussions about that, especially about fields change
their types.

>
> Structured Bindings would be acceptable if, like scripting languages, we
> didn't have anything else to work with.
>
> But we do: We can return a struct.

Structured bindings work with structs as well. If you think structured
bindings mean
you must return a tuple, you're mistaken.

> I fear Structured Bindings will lead to an explosion of *really* bad API that
> returns std::pair or std::tuple when it should return a small struct with

C++ programmers are not idiots.

D. B.

unread,
Oct 5, 2016, 6:44:11 AM10/5/16
to std-pr...@isocpp.org
The feature is already accepted. Only an NB comment can stop it now. Are you a member of an NB?

Ville Voutilainen

unread,
Oct 5, 2016, 6:45:55 AM10/5/16
to ISO C++ Standard - Future Proposals
On 5 October 2016 at 13:44, D. B. <db0...@gmail.com> wrote:
> The feature is already accepted. Only an NB comment can stop it now. Are you
> a member of an NB?

For what it's worth, C++0x concepts were also accepted in a CD ballot.
NB comments are not
the only way to get facilities out of the standard.

Marc Mutz

unread,
Oct 5, 2016, 6:58:12 AM10/5/16
to std-pr...@isocpp.org, Ville Voutilainen, Herb Sutter, Bjarne Stroustrup, Gabriel Dos Reis
On Wednesday 05 October 2016 12:40:38 Ville Voutilainen wrote:
> On 5 October 2016 at 13:38, Marc Mutz <marc...@kdab.com> wrote:
> > In conjunction with auto deduction, the caller choosing the names means
> > that the code becomes brittle in the face of changes to the return type,
> > e.g. when reordering fields to fill padding holes.
>
> There have been discussions about that, especially about fields change
> their types.

And? What was the outcome?

> > Structured Bindings would be acceptable if, like scripting languages, we
> > didn't have anything else to work with.
> >
> > But we do: We can return a struct.
>
> Structured bindings work with structs as well. If you think structured
> bindings mean
> you must return a tuple, you're mistaken.

Yes, I know that. But if I return a struct with proper names, I don't need SB
to handle it. Indeed, it would be counter-productive to have to find names for
SB if can access the names the implementor chose with C-space in my IDE from
the auto variable that stores the return type.

> > I fear Structured Bindings will lead to an explosion of *really* bad API
> > that returns std::pair or std::tuple when it should return a small
> > struct with
>
> C++ programmers are not idiots.

C++ programmers are humans, though.

We all make mistakes. Even Alex Stepanov did. The art is to not repeat them.

Domen Vrankar

unread,
Oct 5, 2016, 7:41:11 AM10/5/16
to std-pr...@isocpp.org, Ville Voutilainen, Herb Sutter, Bjarne Stroustrup, Gabriel Dos Reis
> > Structured Bindings would be acceptable if, like scripting languages, we
> > didn't have anything else to work with.
> >
> > But we do: We can return a struct.
>
> Structured bindings work with structs as well. If you think structured
> bindings mean
> you must return a tuple, you're mistaken.

Yes, I know that. But if I return a struct with proper names, I don't need SB
to handle it. Indeed, it would be counter-productive to have to find names for
SB if can access the names the implementor chose with C-space in my IDE from
the auto variable that stores the return type.

 
So you're saying that returning a structure with error status and result or multiple results of which you only need one or two (reason being for eg. cheaper to return all at once - due to for e.g. web service call or complex processing - than request one by one on need to basis) you would still prefer dragging the entire structure around giving the feeling that your code has more dependencies than it actually does? Pattern matching would probably be better for some cases but it's sometimes nice to have a simpler tool handy.
 
> > I fear Structured Bindings will lead to an explosion of *really* bad API
> > that returns std::pair or std::tuple when it should return a small
> > struct with
>
> C++ programmers are not idiots.

C++ programmers are humans, though.

We all make mistakes. Even Alex Stepanov did. The art is to not repeat them.

 
This sounds like an argument for not having templates, pointers, exceptions, probably even references or any other feature for that matter.

I have ideas on where to use this feature but like with all other features it'd be pretty odd to sprinkle it around the code just because I can and make odd library apis. Good, poor and all in between designs are already present so I doubt that this feature will change that too much but I might be wrong :)

Regards,
Domen

Marc Mutz

unread,
Oct 5, 2016, 8:09:43 AM10/5/16
to std-pr...@isocpp.org, Herb Sutter, Bjarne Stroustrup, Gabriel Dos Reis
On Wednesday 05 October 2016 13:40:20 Domen Vrankar wrote:
> > > > Structured Bindings would be acceptable if, like scripting languages,
> >
> > we
> >
> > > > didn't have anything else to work with.
> > > >
> > > > But we do: We can return a struct.
> > >
> > > Structured bindings work with structs as well. If you think structured
> > > bindings mean
> > > you must return a tuple, you're mistaken.
> >
> > Yes, I know that. But if I return a struct with proper names, I don't
> > need SB
> > to handle it. Indeed, it would be counter-productive to have to find
> > names for
> > SB if can access the names the implementor chose with C-space in my IDE
> > from
> > the auto variable that stores the return type.
>
> So you're saying that returning a structure with error status and result or
> multiple results of which you only need one or two (reason being for eg.
> cheaper to return all at once - due to for e.g. web service call or complex
> processing - than request one by one on need to basis) you would still
> prefer dragging the entire structure around giving the feeling that your
> code has more dependencies than it actually does? Pattern matching would
> probably be better for some cases but it's sometimes nice to have a simpler
> tool handy.

One of us is not underatanding SB correctly, or I don't understand you. SB
stores the whole struct, too, it just defines local aliases to the member of
the struct (or tuple, or pair, or ...).

> > > > I fear Structured Bindings will lead to an explosion of *really* bad
> >
> > API
> >
> > > > that returns std::pair or std::tuple when it should return a small
> > > > struct with
> > >
> > > C++ programmers are not idiots.
> >
> > C++ programmers are humans, though.
> >
> > We all make mistakes. Even Alex Stepanov did. The art is to not repeat
> > them.
>
> This sounds like an argument for not having templates, pointers,
> exceptions, probably even references or any other feature for that matter.

No, it sounds like and argument for checking a feature for how easy it is to
use correctly (SB: very easy) and how hard it is to use incorrectly (SB: very
easy, too), and how much new expressiveness it enables (SB: none(?)).

I see references to pattern matching everywhere I look for SB, and maybe PM
will add more expressiveness, but then let's wait for PM and not add SB, which
does not add enough expressiveness to warrant the drawbacks. SB _is_ touted as
a way to paper over APIs that trade in tuples and pairs. And for that, IMHO,
it is too large a sword for too small a Gordian Knot. If there are other use-
cases, they were notably absent from P0144r2.

> I have ideas on where to use this feature but like with all other features
> it'd be pretty odd to sprinkle it around the code just because I can and
> make odd library apis. Good, poor and all in between designs are already
> present so I doubt that this feature will change that too much but I might
> be wrong :)

And *that* sounds like a call to not pay attention to how a feature can be
abused, because you can abuse any feature. :)

Ville Voutilainen

unread,
Oct 5, 2016, 8:42:28 AM10/5/16
to Marc Mutz, ISO C++ Standard - Future Proposals, Herb Sutter, Bjarne Stroustrup, Gabriel Dos Reis
On 5 October 2016 at 14:00, Marc Mutz <marc...@kdab.com> wrote:
> On Wednesday 05 October 2016 12:40:38 Ville Voutilainen wrote:
>> On 5 October 2016 at 13:38, Marc Mutz <marc...@kdab.com> wrote:
>> > In conjunction with auto deduction, the caller choosing the names means
>> > that the code becomes brittle in the face of changes to the return type,
>> > e.g. when reordering fields to fill padding holes.
>>
>> There have been discussions about that, especially about fields change
>> their types.
>
> And? What was the outcome?

We'll see after the Issaquah meeting. There's an NB comment about it,
I can probably
follow it up with a paper.

Viacheslav Usov

unread,
Oct 5, 2016, 9:11:30 AM10/5/16
to ISO C++ Standard - Future Proposals, hsu...@microsoft.com, bja...@stroustrup.com, g...@microsoft.com
On Wed, Oct 5, 2016 at 12:38 PM, Marc Mutz <marc...@kdab.com> wrote:

> The reaons it's horrible, is because it reverses the C++ principle that the implementor of a library should have to do the work, not the user.

There is no such principle. Because what you say is impossible. A user of a library always has to do some work to use the library.

What you probably wanted to say is that the library should do something that reduces the amount of work done by the prospective user of the library compared to the amount of work required without the library. But then this statement is trivial, because no one will use a library the use of which requires more work than its non-use.

> It reverses the principle by requiring the *caller* to choose the names of the values returned, when it should be the implementor of the function who chooses the names.

If there is a "horrible" library that makes coming up with good names, and typing them, an effort outweighing the utility of the library, it will have to evolve or die.

> In conjunction with auto deduction, the caller choosing the names means that the code becomes brittle in the face of changes to the return type, e.g. when reordering fields to fill padding holes.

That is a valid point, but a very similar problem exists with functions accepting multiple arguments. One can easily come up with horrible (contrived) examples, but, practically, I do not think this is a major issue.

Cheers,
V.

Greg Marr

unread,
Oct 5, 2016, 9:49:33 AM10/5/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 5, 2016 at 8:09:43 AM UTC-4, Marc Mutz wrote:
On Wednesday 05 October 2016 13:40:20 Domen Vrankar wrote:
> So you're saying that returning a structure with error status and result or
> multiple results of which you only need one or two (reason being for eg.
> cheaper to return all at once - due to for e.g. web service call or complex
> processing - than request one by one on need to basis) you would still
> prefer dragging the entire structure around giving the feeling that your
> code has more dependencies than it actually does?

One of us is not underatanding SB correctly, or I don't understand you. SB
stores the whole struct, too, it just defines local aliases to the member of
the struct (or tuple, or pair, or ...).

Only if you say that you want references, using "auto &" or "auto &&".  If you use "auto", you just get copies.

  • If the assignment-expression is a prvalue, e is an xvalue that designates a temporary object copy-initialized from the assignment-expression. The lifetime of the temporary ends immediately after the last variable declaration (see below) if no & follows the decl-specifier-seq in the decomposition declaration, and is extended as usual (12.2 [class.temporary] otherwise.

Ville Voutilainen

unread,
Oct 5, 2016, 10:07:29 AM10/5/16
to ISO C++ Standard - Future Proposals
On 5 October 2016 at 16:49, Greg Marr <greg...@gmail.com> wrote:
> On Wednesday, October 5, 2016 at 8:09:43 AM UTC-4, Marc Mutz wrote:
>>
>> On Wednesday 05 October 2016 13:40:20 Domen Vrankar wrote:
>> > So you're saying that returning a structure with error status and result
>> > or
>> > multiple results of which you only need one or two (reason being for eg.
>> > cheaper to return all at once - due to for e.g. web service call or
>> > complex
>> > processing - than request one by one on need to basis) you would still
>> > prefer dragging the entire structure around giving the feeling that your
>> > code has more dependencies than it actually does?
>>
>> One of us is not underatanding SB correctly, or I don't understand you. SB
>> stores the whole struct, too, it just defines local aliases to the member
>> of
>> the struct (or tuple, or pair, or ...).
>
>
> Only if you say that you want references, using "auto &" or "auto &&". If
> you use "auto", you just get copies.


You get a copy of the whole struct, not copies of individual bindings.

Bjarne Stroustrup

unread,
Oct 5, 2016, 10:11:32 AM10/5/16
to Marc Mutz, hsu...@microsoft.com, g...@microsoft.com, std-pr...@isocpp.org
I think you overestimate the likelihood for problems and underestimate
the convenience offered. The technical points you made were (of course)
considered, repeatedly and seriously.

Yes, you can accidentally reverse the order of naming, just as you can
for function arguments. If the members/arguments are of the same type,
the consequences can be very bad. That's why I don't recommend
interfaces with multiple consecutive arguments of the same type.

You already have the equivalent problems with return types that are
pairs and tuples, and if you don't like structured binding, don't use
it. That's easier done than the equivalent advice for pairs and tuples.

I don't see the problems you outline becoming a major problem, but I do
see the convenience as significant. There is always a balance to be
struck between safety and convenience. Note that languages with a heavy
emphasis on safety tend not to be used for general programming (most
programmers don't like them).

Herb Sutter

unread,
Oct 5, 2016, 10:14:05 AM10/5/16
to Marc Mutz, bja...@stroustrup.com, Gabriel Dos Reis, std-pr...@isocpp.org
> I heard about Structured Bindings for the first time at CppCon this year, and I'd
> like to raise my concerns, esp. since half of the panel was crazy about them.

Thanks for your interest! Did you read the design paper? It answers many of these questions.


> The reaons it's horrible,

Note that you're making a strong value judgment about something you heard about two weeks ago. You're coming into a design discussion that has been discussed in depth at multiple meetings for nearly a year, and while new information is welcome it's always good to enter a discussion-in-progress by treading lightly (e.g., start by asking whether something has been considered). Especially when "half a panel" of people who have been in the deep design discussions and understand the feature like it a lot. :)

> is because it reverses the C++ principle that the
> implementor of a library should have to do the work, not the user.
>
> It reverses the principle by requiring the *caller* to choose the names of the
> values returned,

The caller always chooses the name of a returned value in their scope:

auto my_name = f();

SB is just giving them a way to gives names to the individual components if they want to work with them directly, instead of a name to the whole composite entity:

auto [my_name_1, my_name_2] = f();


> when it should be the implementor of the function who
> chooses the names.

You can have your cake and eat it too, because SB works with structs:

> IMHO, the correct solution to the multiple return value problem is to return a
> struct with properly named fields, not a pair and not a tuple.

(Actually IMO the correct solution is multiple return values. Returning a struct is our current workaround, the closest approximation in today's language.)

That's fine, you can do that, and it works fine with SB. . Here's an example from my own recent gcpp library, https://github.com/hsutter/gcpp:

struct contains_info_ret {
gpage_find_result found;
std::size_t location;
std::size_t start_location;
};
contains_info_ret
contains_info(gsl::not_null<const byte*> p) const noexcept;

The only thing that C++ doesn't currently let you do is to omit the "; contains_info_ret" in the middle, to declare a struct in the position of a return value. From time to time people suggest allowing that; I wouldn't be surprised to see it proposed in the future. (I would like to see that proposal. I would even more like to see a multiple return values proposal.)

But SB works fine with this, because it allows the caller to write both this:

auto info = page.contains_info(ptr);
if (info.found == /*...*/) {
use(info.location, info.start_location);
}

and this:

auto [found, offset, start] info = page.contains_info(ptr);
if (found == /*...*/) {
use(offset, start);
}

Returning a struct should be thought of as the callee returning "default" names for the caller to use. With SB, the caller has a choice and can either use the default names or else provide more meaningful names if they want to; it doesn't hurt your preference for returning a struct at all, in fact it cooperates very well with that -- and that's not an accident, it's by design since the very first design paper.

Herb

Greg Marr

unread,
Oct 5, 2016, 10:20:03 AM10/5/16
to ISO C++ Standard - Future Proposals
That's not what's described in the paper.  Has it changed since https://isocpp.org/files/papers/p0217r0.html ?
The binding results in individual variables, and if they're copies of the values from the struct, there is no reason to keep the original struct around.

Greg Marr

unread,
Oct 5, 2016, 10:23:19 AM10/5/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 5, 2016 at 10:14:05 AM UTC-4, Herb Sutter wrote:
But SB works fine with this, because it allows the caller to write both this:

        auto info = page.contains_info(ptr);
        if (info.found == /*...*/) {
                use(info.location, info.start_location);
        }

and this:

        auto [found, offset, start] info = page.contains_info(ptr);
        if (found == /*...*/) {
                use(offset, start);
        }

Is the "info" in the first line of the second example a typo?  It doesn't seem like it belongs there.

Ville Voutilainen

unread,
Oct 5, 2016, 10:26:09 AM10/5/16
to ISO C++ Standard - Future Proposals
On 5 October 2016 at 17:20, Greg Marr <greg...@gmail.com> wrote:
>> > Only if you say that you want references, using "auto &" or "auto &&".
>> > If
>> > you use "auto", you just get copies.
>>
>> You get a copy of the whole struct, not copies of individual bindings.
>
>
> That's not what's described in the paper. Has it changed since

That's what the wording of the facility does, and what the clang
implementation does.

> https://isocpp.org/files/papers/p0217r0.html ?

See http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0217r1.html


> The binding results in individual variables, and if they're copies of the
> values from the struct, there is no reason to keep the original struct
> around.

Except that yes there is, because if the fields depend on each other,
copying them individually
can break invariants.

Greg Marr

unread,
Oct 5, 2016, 10:36:07 AM10/5/16
to ISO C++ Standard - Future Proposals
On Wednesday, October 5, 2016 at 10:26:09 AM UTC-4, Ville Voutilainen wrote:
On 5 October 2016 at 17:20, Greg Marr <greg...@gmail.com> wrote:
>> > Only if you say that you want references, using "auto &" or "auto &&".
>> > If
>> > you use "auto", you just get copies.
>>
>> You get a copy of the whole struct, not copies of individual bindings.
>
>
> That's not what's described in the paper.  Has it changed since

That's what the wording of the facility does, and what the clang
implementation does.

> https://isocpp.org/files/papers/p0217r0.html ?

See http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0217r1.html

Thanks.  The relevant parts of that paper that significantly change this feature from the one I referenced before:
 
That paper incorporates feedback from the EWG session in Jacksonville as follows:
      • introduced variables are, in all cases, references to the value of the initializer
      • the cv-qualifiers and ref-qualifier of the decomposition declaration are applied to the reference introduced for the initializer, not for the individual member aliases

    Nicol Bolas

    unread,
    Oct 5, 2016, 12:14:44 PM10/5/16
    to ISO C++ Standard - Future Proposals, hsu...@microsoft.com, bja...@stroustrup.com, g...@microsoft.com
    On Wednesday, October 5, 2016 at 6:36:35 AM UTC-4, Marc Mutz wrote:
    Hi Herb, Bjarne, Gabriel,

    I heard about Structured Bindings for the first time at CppCon this year, and
    I'd like to raise my concerns, esp. since half of the panel was crazy about
    them.

    I strongly believe that the premise that returning std::tuple from functions
    to solve the multiple-return-types problem is good practice, is fundamentally
    wrong.

    It is not good in any sense of the word. It is horrible. Indeed, it is so
    horrible that you felt inclined to add a new language feature just to make it
    bearable.

    The reaons it's horrible, is because it reverses the C++ principle that the
    implementor of a library should have to do the work, not the user.

    It reverses the principle by requiring the *caller* to choose the names of the
    values returned, when it should be the implementor of the function who chooses
    the names.

    In conjunction with auto deduction, the caller choosing the names means that
    the code becomes brittle in the face of changes to the return type, e.g. when
    reordering fields to fill padding holes.

    Structured binding is hardly the first C++ feature that makes struct field ordering important. Aggregate initialization did that long before. If you "reorder fields to fill padding holes" on an interface structure, then you've broken any code that used aggregate initialization on that type.

    Now yes, you can fix that by giving that type constructors that mimic the previous ordering. But then again, you can fix similar changes with structured binding by giving that type `tuple_size`/etc functions that mimic the previous ordering as well. So in both cases, we have the tools to make such changes backwards compatibly.

    Structs are ordered collections of named objects. Ordering is a fundamental part of a struct, and structured binding is not the only C++ feature that relies on the order of a struct's members.

    Structured Bindings would be acceptable if, like scripting languages, we
    didn't have anything else to work with.

    But we do: We can return a struct.

    IMHO, the correct solution to the multiple return value problem is to return a
    struct with properly named fields, not a pair and not a tuple.

    Then let us consider the question of "properly named fields". "Properly named" for whom, exactly?

    The function returns values that have some meaning. But the caller gives those values added meaning as well. Consider your `map::insert` code, where you return a struct of two values. One is called "inserted" and the other is called "iterator".

    Well... what does "iterator" mean? To `map::insert`, it merely means the iterator where the item is inserted. But that item that was inserted has a semantic meaning to the caller of `map::insert`. Therefore, the iterator where that item was inserted also has a semantic meaning: the position of that item.

    A meaning which is not reflected in the generic "result.iterator" name you give it. This means that the person reading the calling code has to remember what `map::insert` returns, as well as the semantic meaning of what was passed to `map::insert`.

    Structured binding allows the calling code to impose semantic meaning to such things. Meaning that the function returning those values cannot possibly know about.

    At its core, this is no different from the ability of functions to give names to parameters. The caller passes some object that often has a name. But to the context of the function being called, that object has a semantic meaning which is different from the one the caller gave it. So the function gets to give it a meaning which expresses the intent of the code being called, rather than the intent of the caller.

    Why should function return values be any different? The called function gives these return values one meaning, and the caller can alter that meaning based on its needs.

    I fear Structured Bindings will lead to an explosion of *really* bad API that
    returns std::pair or std::tuple when it should return a small struct with
    well-named data members instead, because a) the implementor couldn't be
    bothered to pick good names for a return struct, and b) tuples can be defined
    on the fly, in the function declaration, whereas structs can not.

    I believe there's a proposal for allowing to define structs in function
    declarations already, but I failed to find it.

    It's P0222. If you read that proposal however, one of its primary motivations is to work in tandem with structured bindings. It was not designed to work against it, but alongside it.

    The idea being that the member names which the anonymous struct uses would be self-documenting as to their meaning. But the caller can still impose their own semantic meaning via structured binding, as they see fit. And that proposal sees as perfectly legitimate.

    So returning anonymous structs is not a replacement for structured binding. We'd still want structured binding even if we could return anonymous structs.

    Bjarne Stroustrup

    unread,
    Oct 5, 2016, 12:30:30 PM10/5/16
    to Nicol Bolas, ISO C++ Standard - Future Proposals, hsu...@microsoft.com, g...@microsoft.com



    On 10/5/2016 12:14 PM, Nicol Bolas wrote:
    On Wednesday, October 5, 2016 at 6:36:35 AM UTC-4, Marc Mutz wrote:
    Hi Herb, Bjarne, Gabriel,
    ...

    Structured binding is hardly the first C++ feature that makes struct field ordering important. Aggregate initialization did that long before. If you "reorder fields to fill padding holes" on an interface structure, then you've broken any code that used aggregate initialization on that type.
    Good point. It has been like this since 1974 or so.

    ...

    Marc Mutz

    unread,
    Oct 5, 2016, 1:06:51 PM10/5/16
    to std-pr...@isocpp.org, 'Herb Sutter' via ISO C++ Standard - Future Proposals, bja...@stroustrup.com, Gabriel Dos Reis
    On Wednesday 05 October 2016 16:13:32 'Herb Sutter' via ISO C++ Standard -
    Future Proposals wrote:
    > > I heard about Structured Bindings for the first time at CppCon this year,
    > > and I'd like to raise my concerns, esp. since half of the panel was
    > > crazy about them.
    >
    > Thanks for your interest! Did you read the design paper? It answers many of
    > these questions.

    If with "design paper" you mean P0144R2, then, yes, I read it and, no, I'm
    afraid it doesn't answer what SB is supposed to be good at except make
    functions returning pairs and tuples bearable, syntax-wise, at the call site.

    My main concern is that you shouldn't use these types in APIs in the first
    place, and they are the current work around for the clumsiness of defining
    structs to hold multiple return values.

    > > The reaons it's horrible,
    >
    > Note that you're making a strong value judgment about something you heard
    > about two weeks ago.

    You misunderstand. The horrible thing is pair and tuple in APIs:

    if(map.insert(...).second)

    second? Second what? This is horrible, and I guess Alex Stepanov would not
    disagree.

    I agree SB solves this, in a way. My premise, though, is that if we stop using
    pair and tuple in apis, the original problem goes away, and the solution for
    the problem becomes obsolete.

    > You're coming into a design discussion that has been
    > discussed in depth at multiple meetings for nearly a year, and while new
    > information is welcome it's always good to enter a discussion-in-progress
    > by treading lightly (e.g., start by asking whether something has been
    > considered). Especially when "half a panel" of people who have been in the
    > deep design discussions and understand the feature like it a lot. :)

    You're right, of course. My apologies.

    > > is because it reverses the C++ principle that the
    > > implementor of a library should have to do the work, not the user.
    > >
    > > It reverses the principle by requiring the *caller* to choose the names
    > > of the values returned,
    >
    > The caller always chooses the name of a returned value in their scope:
    >
    > auto my_name = f();
    >
    > SB is just giving them a way to gives names to the individual components if
    > they want to work with them directly, instead of a name to the whole
    > composite entity:
    >
    > auto [my_name_1, my_name_2] = f();

    This works fine with a very few (2) items returned. Just as function arguments
    work best if there are just a few.

    In both cases, if there are more than just a few, the code becomes brittle. It
    doesn't matter whether you pass five values into a function or use SB to get
    them out: postitional arguments are bad for that. For years, a recurring theme
    is named function arguments. *I* never missed them. If a function takes too
    many arguments to a function, you refactor it to make it take a struct/class.
    If you don't, the code becomes unreadable.

    Take this example of Qt3's QSlider constructor:

    m_slider = new QSlider(0, 100, 10, 0, Qt::Horizontal, this, "m_slider");

    In Qt4, this ctor was deprecated. You now have to say:

    m_slider = new QSlider(this);
    m_slider->setObjectName("m_slider");
    m_slider->setRange(0, 100);
    m_slider->setPageStep(10);
    m_slider->setValue(0);
    m_slider->setOrientation(Qt::Horizontal);

    This was also possible in Qt3, of course. Except we're programmers, and we're
    lazy, so everyone (me included) tended to use the terse constructor call.

    SB is a bit like that ctor. Returning a struct is a bit like that Qt4 code:
    The library authors choose the function names, same as they choose the member
    names of the return struct. By forcing the use of the same name for the same
    functionality in user code, familiarity with the API is built. Code using the
    library becomes easier to read. Someone familiar with the API will be able to
    understand the code better, even if someone else wrote it.

    By allowing every user to choose the names of return values, code becomes
    harder to understand, because there's no single correct name for each field.

    > > when it should be the implementor of the function who
    > > chooses the names.
    >
    > You can have your cake and eat it too, because SB works with structs:
    > > IMHO, the correct solution to the multiple return value problem is to
    > > return a struct with properly named fields, not a pair and not a tuple.
    >
    > (Actually IMO the correct solution is multiple return values. Returning a
    > struct is our current workaround, the closest approximation in today's
    > language.)

    See above for why I think this is not a good idea. Positional arguments are
    generally inferior to named ones. Positional arguments are easy to write, but
    hard to read. It's the library-enforced required names of functions (and
    fields) that anchor and guide the reader through unfamiliar code:
    "setObjectName? Ok, not interesting, skip that.". You might make a case for
    multiple return values as complementing function arguments, but they will not
    help write better APIs, I think.


    > That's fine, you can do that, and it works fine with SB. . Here's an
    > example from my own recent gcpp library, https://github.com/hsutter/gcpp:
    >
    > struct contains_info_ret {
    > gpage_find_result found;
    > std::size_t location;
    > std::size_t start_location;
    > };
    > contains_info_ret
    > contains_info(gsl::not_null<const byte*> p) const noexcept;

    Even with so much effort going into making the Qt 4 API more readable, at
    roughly the same time, this slipped in:

    typedef QPair<double, QColor> QGradientStop;
    typedef QVector<QGradientStop> QGradientStops;

    Which just goes to prove that pairs (and, by extension, tuples) are addictive.

    > The only thing that C++ doesn't currently let you do is to omit the ";
    > contains_info_ret" in the middle, to declare a struct in the position of a
    > return value. From time to time people suggest allowing that; I wouldn't
    > be surprised to see it proposed in the future. (I would like to see that
    > proposal. I would even more like to see a multiple return values
    > proposal.)
    >
    > But SB works fine with this, because it allows the caller to write both
    > this:
    >
    > auto info = page.contains_info(ptr);
    > if (info.found == /*...*/) {
    > use(info.location, info.start_location);
    > }
    >
    > and this:
    >
    > auto [found, offset, start] info = page.contains_info(ptr);
    > if (found == /*...*/) {
    > use(offset, start);
    > }
    >
    > Returning a struct should be thought of as the callee returning "default"
    > names for the caller to use. With SB, the caller has a choice and can
    > either use the default names or else provide more meaningful names if they
    > want to; it doesn't hurt your preference for returning a struct at all, in
    > fact it cooperates very well with that -- and that's not an accident, it's
    > by design since the very first design paper.

    I get that it works with structs, too. And I appreciate it. My problem is that
    it works with, and in a way legitimises, returning pairs and tuples instead,
    too. Yes, if SB works with structs, it's more than natural to have it work
    with tuples and pairs, too, but P0144R2 makes it very clear that the rationale
    goes the other way around. Quoting the abstract:

    > Abstract
    > This paper proposes the ability to
    > store a value and bind names to its components, along the lines of:
    > tuple<T1,T2,T3> f(/*...*/) { /*...*/ return {a,b,c}; }
    > auto [x,y,z] = f(); // x has type T1, y has type T2, z has type T3
    > This addresses the requests for support of returning multiple values, which
    > has become a popular request lately.
    > Proposed wording appears in a separate paper, P0217.

    This example would look ridiculous if you had used a struct as the return
    value of f():

    struct { T1 a; T2 b; T3 c; } f(/*...*/) { /*...*/ return {a,b,c}; }
    auto [x,y,z] = f(); // x has type T1, y has type T2, z has type T3

    "Huh? Why would I ever want to name 'a' 'x', 'b' 'y' etc? I can't change the
    name of 'f', either."

    The paper goes on to say:

    > Today, we allow multiple return values via tuple
    > pretty nicely in function declarations and definitions

    No. Tuples as a vehicle to return multiple values from a funciton are *not*
    nice. They are convenient, yes. For the function author. But no-one else.
    Maybe with variadic content, but that's just another way to say that we need a
    way to expand packs into data members. No, the basic premise is wrong, IMHO,
    so SB becomes a solution for a problem that I believe is not worth solving,
    and offers little-to-no benefit (granted, that I can see, and my view might be
    limited) for libraries that return structs.

    If there's a grander plan than that to transition smoothly from STL's
    std::pair API to something hopefully better, I'd like to hear it. But there
    must be better ways to achieve this. structs that implicitly convert to pairs
    are one way, but break code that accesses the members directly:

    if (map.insert(...).second)

    For that, maybe 'using' could be (ab)used:

    template <typename Iterator>
    struct map_insert_result {
    Iterator iterator;
    bool inserted;
    [[deprecated]] using first = iterator;
    [[deprecated]] using second = inserted;
    [[deprecated]] operator std::pair<Iterator, bool>() const
    { return {iterator, inserted}; }
    };

    That would help with migrating users to a better QGradientStop, too.

    Thanks,
    Marc

    Nicol Bolas

    unread,
    Oct 5, 2016, 6:07:04 PM10/5/16
    to ISO C++ Standard - Future Proposals, bja...@stroustrup.com, g...@microsoft.com
    On Wednesday, October 5, 2016 at 1:06:51 PM UTC-4, Marc Mutz wrote:
    On Wednesday 05 October 2016 16:13:32 'Herb Sutter' via ISO C++ Standard -
    Future Proposals wrote:
    > > I heard about Structured Bindings for the first time at CppCon this year,
    > > and I'd like to raise my concerns, esp. since half of the panel was
    > > crazy about them.
    >
    > Thanks for your interest! Did you read the design paper? It answers many of
    > these questions.

    If with "design paper" you mean P0144R2, then, yes, I read it and, no, I'm
    afraid it doesn't answer what SB is supposed to be good at except make
    functions returning pairs and tuples bearable, syntax-wise, at the call site.

    My main concern is that you shouldn't use these types in APIs in the first
    place, and they are the current work around for the clumsiness of defining
    structs to hold multiple return values.

    > > The reaons it's horrible,
    >
    > Note that you're making a strong value judgment about something you heard
    > about two weeks ago.

    You misunderstand. The horrible thing is pair and tuple in APIs

    The problem I find with your argument is this: even if we assume you're right, that tuples should not be used as a way to return multiple values ever, structured binding is still a useful tool. And we will still want structured binding to be extensible to user-defined types with private members. And that will include `pair` and `tuple`.

    The tool may encourage certain misuse; that much is true. But it is far too useful of a tool to disregard it just because of potential misuse.

    You essentially are making a blanket statement that pairs and tuples are inherently confusing. I contest this, and you've given a perfect example of when their meaning is well understood:


    typedef QPair<double, QColor> QGradientStop;

    Just from the name of the type and the types of the arguments (and of course the associated domain knowledge), I know exactly what these values mean. So when I see this:

    QGradientStop s = ...
    if(get<double>(s) < dist)
    {
     
    ...
    }

    We know what it is doing. Could this code be more clear if it were a struct with named members? Probably. But it isn't un-clear as it currently stands; it simply could be more clear. That is, the code isn't bad as written; it simply may be open to improvement.

    There's a lot of space between "not good" and "bad." And in many cases, pair/tuple use, while "not good", doesn't descend to the level of "bad".

    And that's good enough.

    Matt Calabrese

    unread,
    Oct 5, 2016, 7:35:09 PM10/5/16
    to ISO C++ Standard - Future Proposals, hsu...@microsoft.com, bja...@stroustrup.com, g...@microsoft.com
    On Wed, Oct 5, 2016 at 3:38 AM, Marc Mutz <marc...@kdab.com> wrote:
    To summarise: I feel that Structured Bindings are too easy to use incorrectly,
    because they put the burden of choosing a name for the fields of a return
    value on the caller, instead of the implementor, of the function. I'd like to
    see the pattern to return structs from functions strengthened, not the anti-
    pattern of returning pairs and tuples.

    Hi Marc,

    People have already addressed your concerns, but you seem to also be missing one of the key, important reasons why tuples are useful to library developers -- specifically, they are necessary when the amount and types of the tuple fields are dependent on template parameters used by a library. In these cases, a library implementor simply *cannot* "create a struct with named members". You are mostly arguing that pairs and tuples are often misused by developers and even by the standard, which is actually a statement that I agree with, however, tuples are still very necessary in generic libraries, and users of those libraries can get a lot of benefit from structured bindings. Structured bindings are also useful not just for tuples, but also for structs, so your argument based on tuple is a bit misguided.

    For an example of a library that cannot simply create a struct with named members, consider Boost.Spirit, which is not at all unique in this respect.

    Marc Mutz

    unread,
    Oct 6, 2016, 5:40:20 AM10/6/16
    to std-pr...@isocpp.org, Nicol Bolas, bja...@stroustrup.com, g...@microsoft.com
    > ever, structured binding is *still a useful tool*. And we will still want
    > structured binding to be extensible to user-defined types with private
    > members. And that will include `pair` and `tuple`.
    >
    > The tool may encourage certain misuse; that much is true. But it is far too
    > useful of a tool to disregard it just because of potential misuse.

    Ok, so I guess I still have to learn what this tool is useful for, except
    returning pairs and tuples. Other than that, and a certain language-
    theoretical symmetry with function arguments, what's so crazy cool about
    aliasing existing names into something else? Because P0144R2 for sure doesn't
    say. It focuses exclusively on the use-case of returning tuples.

    > You essentially are making a blanket statement that pairs and tuples are
    > inherently confusing. I contest this, and you've given a perfect example of
    > when their meaning is well understood:
    >
    > typedef QPair<double, QColor> QGradientStop;
    >
    > Just from the name of the type and the types of the arguments (and of
    > course the associated domain knowledge), I know exactly what these values
    > mean. So when I see this:
    >
    > QGradientStop s = ...
    > if(get<double>(s) < dist)
    > {
    > ...
    > }
    >
    > We know what it is doing.

    Do we? What's that double? You need to know QGradientStop's definition to know
    what that double is. If QGradientStop would be

    struct QGradientStop {
    double location;
    QColor color;
    };

    then

    if (stop.location < dist)

    would be self-explanatory. This doesn't matter much if you're confronted with
    a small library such as the STL(!), but when you get to large-scale libraries
    such as Qt (or Boost), this stuff is at the center of readability.

    And what do you do in this case:

    struct GammaCorrectionStop {
    uchar from;
    uchar to;
    };

    ?

    > Could this code be more clear if it were a struct
    > with named members? Probably.

    Definitely.

    > But it isn't *un*-clear as it currently
    > stands; it simply could be *more* clear. That is, the code isn't *bad* as
    > written; it simply may be open to improvement.

    I disagree. The fact that I need to call get<double>, a) that I need to call a
    get function, b) that I need to specify the _type_, in the era of auto
    variables, already disqualifies this for many users. Putting the user hat on,
    I would loudly protest if I had to write convoluted code like that only to
    access a member that the library author was too lazy to name. It could be
    improved somewhat (independent of naming) by defining a special type for a
    position on the gradient ray:

    struct QGradientRayPosition { double percent; };

    and then further, by making 'percent' a quantity.

    But each such small struct, while I'm all for them, also carries a cost:
    documentation needs to be written, type-traits need to be specialised
    (is_trivially_relocatable, e.g.), tests need to be written.

    It's so much easier to say "I return a tuple, then I document the fields in
    the docs of the function returning the tuple". If you find that derogative,
    look at libkdepim/libkleo: I did that, too, so I don't exclude myself here.

    > There's a lot of space between "not good" and "bad." And in many cases,
    > pair/tuple use, while "not good", doesn't descend to the level of "bad".
    >
    > And that's good enough.

    But is "good enough" good enough a reason to add it to the standard? We have
    bind, then came lambdas. Some bind expressions still look better than their
    corresponding lambda equivalents, as do some Boost.Lambda expressions, btw,
    but by and large, bind() and Boost.Lambda are dead with C++14 polymorphic
    lambdas.

    It seems to me that the same will be true of tuples and structured bindings
    once C++ gets the missing pieces: data member generation from template packs
    (as part of reflection?), structs declared within the function declaration,
    maybe Herb's multiple return values,...

    I have greeted every new C++ feature since its inception with "cool stuff", I
    even liked auto_ptr for all its shortcomings, but SB is the first one that
    gives me pause. P0144R2 didn't change my mind. What's the fuss about? What's
    the bigger picture I'm not seeing here?

    TONGARI J

    unread,
    Oct 6, 2016, 8:42:41 AM10/6/16
    to ISO C++ Standard - Future Proposals, jmck...@gmail.com, bja...@stroustrup.com, g...@microsoft.com
    On Thursday, October 6, 2016 at 5:40:20 PM UTC+8, Marc Mutz wrote:

    [...] 

    It seems to me that the same will be true of tuples and structured bindings
    once C++ gets the missing pieces: data member generation from template packs
    (as part of reflection?), structs declared within the function declaration,
    maybe Herb's multiple return values,...

    I have no idea how the multiple return values that Herb mentioned will differ from SB. A language that has built-in support for multiple return values, e.g. Go, looks exactly like SB in C++: https://gobyexample.com/multiple-return-values

    BTW, a language feature I'm currently developing will allow you to do something like: 

    std::tuple<int.a, int.b> f()
    {
       
    return {0, 1};
    }

    auto t = f();
    assert(t.a == 0);
    assert(t.b == 0);

    Is this what you want?

    Nicol Bolas

    unread,
    Oct 6, 2016, 9:36:47 AM10/6/16
    to ISO C++ Standard - Future Proposals, jmck...@gmail.com, bja...@stroustrup.com, g...@microsoft.com
    On Thursday, October 6, 2016 at 5:40:20 AM UTC-4, Marc Mutz wrote:
    On Thursday 06 October 2016 00:07:03 Nicol Bolas wrote:
    > On Wednesday, October 5, 2016 at 1:06:51 PM UTC-4, Marc Mutz wrote:
    > > On Wednesday 05 October 2016 16:13:32 'Herb Sutter' via ISO C++ Standard
    > >

    No, you don't. You just have to think about it and know what a gradient stop is conceptually.

    A "gradient" represents a gradual change between two or more locations, commonly called "stops". Therefore, each such "stop" must have two values: a position and the color at that position. The type `double` is certainly not a color, so by process of elimination, it must be the position.

    And what do you do in this case:

       struct GammaCorrectionStop {
           uchar from;
           uchar to;
       };

    ?

    ... I'm not sure I understand what that case is supposed to represent. Nobody is suggesting the wholesale replacement of all structs with tuples. Nobody is suggesting that every two-member struct should be replaced with a pair of two types. Indeed, I thought I made it quite clear that not all tuple usage is reasonably clear.

    I'm simply arguing against your idea that all `tuple` use is a priori bad.

    > There's a lot of space between "not good" and "bad." And in many cases,
    > pair/tuple use, while "not good", doesn't descend to the level of "bad".
    >
    > And that's good enough.

    But is "good enough" good enough a reason to add it to the standard?

    for(auto[x, y] : zip(vector1, vector2))
    {
     
    ...
    }

    The ability to do that alone justifies the feature. `zip` here combines the given ranges into a single range over a tuple of values. Which of course goes back to Matt's point about the utility of tuples in generic contexts.

    Is this code hard to understand? Is it difficult to know what the return of that `zip` range will be? Or what the return of a zip iterator will be? No; so long as you know what `zip` does, this code is quite readable.
     
    We have
    bind, then came lambdas. Some bind expressions still look better than their
    corresponding lambda equivalents, as do some Boost.Lambda expressions, btw,
    but by and large, bind() and Boost.Lambda are dead with C++14 polymorphic
    lambdas.

    It seems to me that the same will be true of tuples and structured bindings
    once C++ gets the missing pieces: data member generation from template packs
    (as part of reflection?), structs declared within the function declaration,
    maybe Herb's multiple return values,...

    If C++20 gets designated initializers, then it becomes possible to have named parameters for functions by using a struct:

    struct params
    {
     
    int x; int y; int z = 2;
    };

    void func(params p);

    func
    ({.x = ..., .y = ...});

    Does that mean that every function will start taking a single struct and force you to write that? Of course not. It will only be done for functions where the number of parameters is large, functions which have a lot of defaults, or functions which it would otherwise be more confusing to take parameters directly than to use named parameters of a struct.

    The same goes for returning a tuple vs. a struct. There are cases where returning a `tuple` is sufficiently self-documenting as to its meaning. And there are cases where it is not. So even if we have the ability to put a struct in a function's return type, people will still return `tuple`s.

    Let's say we added the ability to make this code work:

    template<typename ...Ts>
    struct {zip_range<Ts> &ranges...;} zip(Ts&& ...ts) {...}

    And on the receiving end:

    auto z_ranges = zip(vector1, vector2);
    z_ranges
    .ranges[0]; //represents vector1

    How is that any more clear to the reader than:

    auto z_ranges = zip(vector1, vector2);
    get<0>(z_ranges); //Represents vector1

    There's no magical reflection that can give the struct version knowledge that the first index corresponds to a parameter who's argument comes from a variable named `vector1`. There is no way that `zip` can possibly give the members of its return value a semantic name. These values only exist as positions relative to the positions of the inputs. Only the caller knows what the meaning of the inputs is, so only the caller can know what the meaning of the outputs will be. So there's no advantage to using a struct instead of a tuple.

    Returning `tuple`s is not going away. As metaprogramming techniques become easier to use, so too will `tuple`s become more commonly used. Best to get used to it.

    Matt Calabrese

    unread,
    Oct 6, 2016, 11:51:04 AM10/6/16
    to ISO C++ Standard - Future Proposals, Jason McKesson, Bjarne Stroustrup, g...@microsoft.com
    On Thu, Oct 6, 2016 at 5:42 AM, TONGARI J <tong...@gmail.com> wrote:
    On Thursday, October 6, 2016 at 5:40:20 PM UTC+8, Marc Mutz wrote:

    [...] 

    It seems to me that the same will be true of tuples and structured bindings
    once C++ gets the missing pieces: data member generation from template packs
    (as part of reflection?), structs declared within the function declaration,
    maybe Herb's multiple return values,...

    I have no idea how the multiple return values that Herb mentioned will differ from SB. A language that has built-in support for multiple return values

    I agree. I thought about this a lot when I first started working on Regular void (for in-depth discussion as to why this came up, you'd have to look at the Regular void paper or the initial std-future-proposals discussion). Basically, any worthwhile multiple-return-value facility in C++ would be equivalent in functionality to tuples. The reason for this is that for such return values to be useful in generic code (or even non-generic code), you'd want that *pack* of results to itself be a first-class object that can be held or forwarded along as a single object. A quick example of why "multiple return values" should be able to be used as a single object, consider something like std::future<decltype(some_function_returning_multiple_values())>. Should this work? I'd certainly hope so. This very entity comes up frequently if you wish to execute a function asynchronously and refer to the result via a future. There are various answers to what the decltype there should yield: a library-level tuple, one of Mike Spertus's parameter pack objects, an actual parameter-pack of multiple types (in other words, a comma separated list of types passed as separate parameters), an error, etc.. Of those options, the ones that end up being most-useful are a tuple-like type or a single parameter pack object. Put another way, either a library-level tuple or a language-level tuple. Other solutions only end up making the facility not very practical. In either case (or the struct case), I'd expect structured bindings to work at the call-site.

    And again, the language will always have a need for library-level or language-level tuple types with unnamed elements, and similarly for library-level or language-level variants with unnamed alternatives. These are necessary any time the number of elements/alternatives are known at compile-time, but are, for instance, dependent on a template parameter. If used correctly, the fact that the elements/alternatives do not have individual names is as much of a "problem" as the elements of any std::vector or of any std::array not having individual names. That isn't to say that sometimes tuples are misused in places where a struct would be better -- that's certainly true, and even so in the standard library. The necessity for such algebraic types is still there, though, and with respect to structured bindings, the facility is useful whether that is acknowledged or not.

    Farid Mehrabi

    unread,
    Oct 6, 2016, 12:22:48 PM10/6/16
    to std-proposals, Jason McKesson, Bjarne Stroustrup, g...@microsoft.com
    My understanding is that from the OP's perspective, built-in tuple or some similar feature would address broader range of solutions than SB and involves less burden on the syntax. But at the same time, lack of multiple return is an obstacle against proper use of ranges in 'for' loops. Am I correct?
    regards,
    FM.

    --
    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-proposals+unsubscribe@isocpp.org.
    To post to this group, send email to std-pr...@isocpp.org.
    To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANh8DEnqePPzAUgBGnST3aDEEyb05fwTvLtThU7z6AUuqbBKwQ%40mail.gmail.com.



    --
    how am I supposed to end the twisted road of  your hair in such a dark night??
    unless the candle of your face does shed some light upon my way!!!

    Nicol Bolas

    unread,
    Oct 6, 2016, 1:57:26 PM10/6/16
    to ISO C++ Standard - Future Proposals, jmck...@gmail.com, bja...@stroustrup.com, g...@microsoft.com
    On Thursday, October 6, 2016 at 12:22:48 PM UTC-4, Farid Mehrabi wrote:
    My understanding is that from the OP's perspective, built-in tuple or some similar feature would address broader range of solutions than SB and involves less burden on the syntax.

    The fundamental difference between a "tuple" and a "struct" is that elements of the former don't have names; they only have positions and types. If tuples were built-in, it would simply be a language feature rather than a library feature. So you might be able to access the elements with more natural syntax than `get<0>`, but you'd still be using `0` rather than `name`. The OP isn't talking about built-in tuples; he's talking about making it easier to define one-off structs for MRV returning.

    What the OP is essentially arguing is that returning objects with named members is always the correct solution to the MRV problem. And given that assumption, there's no point in using structured binding.

    I find neither of these positions to be particularly compelling. I agree that tuples and pairs can be (have been, and currently are) misused. But there are places where tuples are very appropriate and cannot reasonably be replaced by `structs` (at least, not in any way that is an improvement). Structured binding is extremely useful when used in tandem with tuples of that sort. And the fact that a tool can be misused cannot by itself override the good that this tool can do.

    Will structured binding make people use tuple types in places where a genuine struct would be more appropriate? Yes. But it encourages plenty of appropriate tuple use as well. And even if we do get unnamed struct returning, we will still use structured binding to catch those multiple return values.

    FrankHB1989

    unread,
    Oct 10, 2016, 12:03:08 AM10/10/16
    to ISO C++ Standard - Future Proposals, jmck...@gmail.com, bja...@stroustrup.com, g...@microsoft.com


    在 2016年10月6日星期四 UTC+8下午5:40:20,Marc Mutz写道:
    On Thursday 06 October 2016 00:07:03 Nicol Bolas wrote:

    But is "good enough" good enough a reason to add it to the standard? We have
    bind, then came lambdas. Some bind expressions still look better than their
    corresponding lambda equivalents, as do some Boost.Lambda expressions, btw,
    but by and large, bind() and Boost.Lambda are dead with C++14 polymorphic
    lambdas.

    No, even polymorphic lambda expressions can't replace `bind` totally, for:
    1. Duplicated placeholders.
    2. Nested bind expressions (subtle different in boost and std, not considered good practice).
    3. More importantly, proper abstraction. There are cases where bind expressions are more preferred than lambda expressions. Bind expression without differences above are essentially forms of partial function application. They can be implemented with or without lambda expressions, but the latter is in the implementation detail. For cases where bind expressions are enough, instead using lambda expressions directly is unnecessarily relying on the leaky abstraction. (Ideally, for typical environments, bind expressions should also be optimized builtins like std::index_sequence.)



     

    Matthew Woehlke

    unread,
    Oct 12, 2016, 10:30:09 AM10/12/16
    to std-pr...@isocpp.org
    (Apologies if this posts twice; the original appears to have gotten lost.)

    On 2016-10-05 13:08, Marc Mutz wrote:
    > Even with so much effort going into making the Qt 4 API more readable, at
    > roughly the same time, this slipped in:
    >
    > typedef QPair<double, QColor> QGradientStop;

    FWIW, this sort of thing needs to be burned with fire anywhere it
    happens. IMHO we need to keep working on the "tuple-like" concept so
    that there is *no reason* to write the above instead of:

    struct QGradientStop { double position; QColor color; };

    I don't think we're there yet. There are (apparently) still cases where
    QPair/std::pair/std::tuple is more useful than a struct. If we can make
    that no longer the case, I hope we can stop (ab)using those for cases
    that a struct is more appropriate.

    --
    Matthew

    Matthew Woehlke

    unread,
    Oct 12, 2016, 10:40:35 AM10/12/16
    to std-pr...@isocpp.org
    (Apologies if this posts twice; the original appears to have gotten lost.)

    On 2016-10-05 06:38, Marc Mutz wrote:
    > I heard about Structured Bindings for the first time at CppCon this year, and
    > I'd like to raise my concerns, esp. since half of the panel was crazy about
    > them.
    >
    > The reaons it's horrible, is because it reverses the C++ principle that the
    > implementor of a library should have to do the work, not the user.
    >
    > It reverses the principle by requiring the *caller* to choose the names of the
    > values returned, when it should be the implementor of the function who chooses
    > the names.
    >
    > Structured Bindings would be acceptable if, like scripting languages, we
    > didn't have anything else to work with.
    >
    > But we do: We can return a struct.
    >
    > IMHO, the correct solution to the multiple return value problem is to return a
    > struct with properly named fields, not a pair and not a tuple.
    >
    > I believe there's a proposal for allowing to define structs in function
    > declarations already, but I failed to find it.

    You are likely thinking of http://wg21.link/p0222 and
    http://wg21.link/p0224. (I still need to merge and dust those off one of
    these days...)

    I agree that P0222 would be *much* better than returning pair/tuple, but
    I also don't have a problem with assignment unpacking. I'm not convinced
    that return values are so much more brittle than parameters that they
    will make API stability much worse.

    (That said, I wouldn't be overly heartbroken if S.B. was held back for a
    revision. I'm not convinced that we've solved bitfields adequately.)

    --
    Matthew

    Reply all
    Reply to author
    Forward
    0 new messages