Unnamed variables through structured binding.

1,423 views
Skip to first unread message

Nicol Bolas

unread,
Jan 11, 2017, 8:34:07 PM1/11/17
to ISO C++ Standard - Future Proposals
Basically, just allow us to do this:

auto [] = whatever;

If you make that legal for all types, then what you effectively have is an unnamed variable. Structured binding/decomposition declarations already require that ability as a side effect of their primary work. We would just be using that side effect directly.

Lazy decomposition already ensures that `get<I>` will only be called when a decomposed name is used. So the idea of allowing any type to have zero decompositions effectively allows any type to work here.

I'm not entirely sure about the grammar for this. If `auto[]` is useful in some way, then that would make this idea non-functional.

So long as the grammar works out, I believe that we would need only two changes to the standard:

1: The ability to specify a decomposition declaration with no names to decompose to. The `identifier-list` in the grammer would simply be made optional.

2: If there are no names in the decomposition declaration, then it skips everything after [dcl.decomp]/1.

And presto: we allow people to have unnamed variables.

Ion Todirel

unread,
Jan 11, 2017, 9:22:29 PM1/11/17
to std-pr...@isocpp.org
Can you provide an example on how you would use the unnamed variable? 

--


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.


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/dae03b23-a771-4844-90dc-d3de39c03d47%40isocpp.org.


TONGARI J

unread,
Jan 12, 2017, 12:51:30 AM1/12/17
to std-pr...@isocpp.org
2017-01-12 9:34 GMT+08:00 Nicol Bolas <jmck...@gmail.com>:
Basically, just allow us to do this:

auto [] = whatever;

If you make that legal for all types, then what you effectively have is an unnamed variable. Structured binding/decomposition declarations already require that ability as a side effect of their primary work. We would just be using that side effect directly.

I'm not against the idea but it's not a general solution for unnamed variable, e.g. "whatever" may not be moveable/copyable.

D. B.

unread,
Jan 12, 2017, 12:57:20 AM1/12/17
to std-pr...@isocpp.org
On Thu, Jan 12, 2017 at 2:22 AM, Ion Todirel <ionto...@gmail.com> wrote:
Can you provide an example on how you would use the unnamed variable?

I think the idea is that you don't, and that such variables are for locking or other tasks where one needs some object to control RAII until the current scope ends, but doesn't need to refer to that object again.

Nicol Bolas

unread,
Jan 12, 2017, 1:24:35 AM1/12/17
to ISO C++ Standard - Future Proposals

... so?

We're talking post-C++17. Which means post-guaranteed elision. It doesn't matter if "whatever" is copyable or moveable. If it's a prvalue, then that will work. And if it's not a prvalue... then what is it you're trying to do? Or at the very least, you can do `auto &&[]`.

FrankHB1989

unread,
Jan 12, 2017, 1:33:11 AM1/12/17
to ISO C++ Standard - Future Proposals


在 2017年1月12日星期四 UTC+8上午9:34:07,Nicol Bolas写道:

And presto: we allow people to have unnamed variables.

The resolution of CWG 1769 had already introduced the concept of "unnamed variable" normatively (but not as a clearly defined term); though I don't think it is a serious and correct design in terminology. Besides the confusion of the meaning about "mutable state" among other languages, the change made it more confusing with the notion of a variable in traditional mathematics (which is always named to be an instance of binding in some context in some usual formal systems as well as composing an environment in flavor of Lisp dialects). Note that ISO C carefully avoids the noun "variable" and using "object" instead.

Zhihao Yuan

unread,
Jan 12, 2017, 2:14:34 AM1/12/17
to std-pr...@isocpp.org
On Wed, Jan 11, 2017 at 7:34 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> auto [] = whatever;

I see 2 problems this suggestion.

1. It has to be a full statement because it is a definition,
so it cannot appear in an expression to extend the lifetime
of the result of a sub-expression, which is an often wanted
feature.

2. If in the future we allow an explicit type T to appear in
place of the `auto` in structured binding, `T [] = expr`
may produce meaningless effects, banning them
will make your seemly natural suggestion unnatural.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://blog.miator.net/

Vicente J. Botet Escriba

unread,
Jan 12, 2017, 2:50:18 AM1/12/17
to std-pr...@isocpp.org
I want unnamed variables and your idea seems a good one as struture binding introduces one.

Vicente

Ville Voutilainen

unread,
Jan 12, 2017, 2:52:54 AM1/12/17
to ISO C++ Standard - Future Proposals
On 12 January 2017 at 09:50, Vicente J. Botet Escriba
<vicent...@wanadoo.fr> wrote:
> Basically, just allow us to do this:
> auto [] = whatever;
> I want unnamed variables and your idea seems a good one as struture binding
> introduces one.

Related previous discussions: https://cplusplus.github.io/EWG/ewg-active.html#35

Vicente J. Botet Escriba

unread,
Jan 12, 2017, 3:08:10 AM1/12/17
to std-pr...@isocpp.org
Le 12/01/2017 à 08:14, Zhihao Yuan a écrit :
> On Wed, Jan 11, 2017 at 7:34 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>> auto [] = whatever;
> I see 2 problems this suggestion.
>
> 1. It has to be a full statement because it is a definition,
> so it cannot appear in an expression to extend the lifetime
> of the result of a sub-expression, which is an often wanted
> feature.
Could you show an example?
> 2. If in the future we allow an explicit type T to appear in
> place of the `auto` in structured binding, `T [] = expr`
> may produce meaningless effects, banning them
> will make your seemly natural suggestion unnatural.
>
These are syntactical issues, that could be important, but what I like
of this proposal is the idea of reusing the unnamed variable of
strucutre binding.

If we adopted ... to mean ignore the others

`T [a, ...] = pt`

then

T [...] = expr;

could already be less confusing.

Of course structutre binding requires some constraints on the rhs, but
nothing forbids us to change it and request in this case the use of ...
as pattern.

In the absence of a better syntax I believe

T [...] = expr;

is the best I have found up to now for unnamed C++ variables.

Vicente

P.S. I prefer to use __ for unnamed variables

T __ = expr;

but this is not backward compatible.

Jonathan Müller

unread,
Jan 12, 2017, 3:40:37 AM1/12/17
to std-pr...@isocpp.org
Why is `__` not backwards compatible? Isn't anything containing `__` reserved?

Jonathan

--
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.

TONGARI J

unread,
Jan 12, 2017, 8:42:39 AM1/12/17
to std-pr...@isocpp.org
2017-01-12 14:24 GMT+08:00 Nicol Bolas <jmck...@gmail.com>:
On Thursday, January 12, 2017 at 12:51:30 AM UTC-5, TONGARI J wrote:
2017-01-12 9:34 GMT+08:00 Nicol Bolas <jmck...@gmail.com>:
Basically, just allow us to do this:

auto [] = whatever;

If you make that legal for all types, then what you effectively have is an unnamed variable. Structured binding/decomposition declarations already require that ability as a side effect of their primary work. We would just be using that side effect directly.

I'm not against the idea but it's not a general solution for unnamed variable, e.g. "whatever" may not be moveable/copyable.

... so?

We're talking post-C++17. Which means post-guaranteed elision.

Good to know that guaranteed elision is a thing.

Nicol Bolas

unread,
Jan 12, 2017, 10:19:41 AM1/12/17
to ISO C++ Standard - Future Proposals


On Thursday, January 12, 2017 at 2:14:34 AM UTC-5, Zhihao Yuan wrote:
On Wed, Jan 11, 2017 at 7:34 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> auto [] = whatever;

I see 2 problems this suggestion.

1. It has to be a full statement because it is a definition,
  so it cannot appear in an expression to extend the lifetime
  of the result of a sub-expression, which is an often wanted
  feature.

This idea is not intending to solve those cases. I brought this up because structured binding has 99% of the infrastructure needed for a commonly-asked-for feature: declaring variables with no accessible/visible names.

The case of extending the lifetime of some sub-expression is a different problem requiring a different solution.

2. If in the future we allow an explicit type T to appear in
  place of the `auto` in structured binding, `T [] = expr`
  may produce meaningless effects, banning them
  will make your seemly natural suggestion unnatural.

What "meaningless effects" would `T [] = expr` produce?

Zhihao Yuan

unread,
Jan 12, 2017, 1:06:24 PM1/12/17
to std-pr...@isocpp.org
On Jan 12, 2017 9:19 AM, "Nicol Bolas" <jmck...@gmail.com> wrote:


On Thursday, January 12, 2017 at 2:14:34 AM UTC-5, Zhihao Yuan wrote:
On Wed, Jan 11, 2017 at 7:34 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> auto [] = whatever;

I see 2 problems this suggestion.

1. It has to be a full statement because it is a definition,
  so it cannot appear in an expression to extend the lifetime
  of the result of a sub-expression, which is an often wanted
  feature.

This idea is not intending to solve those cases. I brought this up because structured binding has 99% of the infrastructure needed for a commonly-asked-for feature: declaring variables with no accessible/visible names.

The case of extending the lifetime of some sub-expression is a different problem requiring a different solution.

But any solution which can extend the lifetime of a sub-expression
(to match the lifetime of a named variable) can smoothly extend
the lifetime of an expression as initializer, making your solution
redundant.


2. If in the future we allow an explicit type T to appear in
  place of the `auto` in structured binding, `T [] = expr`
  may produce meaningless effects, banning them
  will make your seemly natural suggestion unnatural.

What "meaningless effects" would `T [] = expr` produce?

bool [] = expr; where expr returns a RAII object convertible
to bool, then the RAII effort loses in vain.

--
Zhihao

Nicol Bolas

unread,
Jan 12, 2017, 1:29:52 PM1/12/17
to ISO C++ Standard - Future Proposals, lic...@gmail.com
On Thursday, January 12, 2017 at 1:06:24 PM UTC-5, Zhihao Yuan wrote:
On Jan 12, 2017 9:19 AM, "Nicol Bolas" <jmck...@gmail.com> wrote:
On Thursday, January 12, 2017 at 2:14:34 AM UTC-5, Zhihao Yuan wrote:
On Wed, Jan 11, 2017 at 7:34 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> auto [] = whatever;

I see 2 problems this suggestion.

1. It has to be a full statement because it is a definition,
  so it cannot appear in an expression to extend the lifetime
  of the result of a sub-expression, which is an often wanted
  feature.

This idea is not intending to solve those cases. I brought this up because structured binding has 99% of the infrastructure needed for a commonly-asked-for feature: declaring variables with no accessible/visible names.

The case of extending the lifetime of some sub-expression is a different problem requiring a different solution.

But any solution which can extend the lifetime of a sub-expression
(to match the lifetime of a named variable) can smoothly extend
the lifetime of an expression as initializer, making your solution
redundant.

The principle advantage of what I'm talking about is that we almost have it already. The wording and so forth for the most complex part has been worked out, and there is viable syntax which already exists. Therefore, the syntactic and standardization burden is minimal.

By contrast, what you're talking about is a completely new construct: arbitrary expression lifetime extension. If that new construct were something that was on the cusp of standardization, I probably wouldn't have made this suggestion. But I don't recall any proposals about generalized expression lifetime extension. I don't even recall any threads on it here.

Whereas we have had several threads about unnamed variables.

2. If in the future we allow an explicit type T to appear in
  place of the `auto` in structured binding, `T [] = expr`
  may produce meaningless effects, banning them
  will make your seemly natural suggestion unnatural.

What "meaningless effects" would `T [] = expr` produce?

bool [] = expr; where expr returns a RAII object convertible
to bool, then the RAII effort loses in vain.

Right, but the results of that would be no different from you doing `bool b = expr;`. So there's no "meaningless effect"; you just did the wrong thing.

Remember: the goal is not to extend the lifetime of `expr`; the goal is to have an unnamed variable.

Zhihao Yuan

unread,
Jan 12, 2017, 1:56:41 PM1/12/17
to Nicol Bolas, ISO C++ Standard - Future Proposals
On Jan 12, 2017 12:29 PM, "Nicol Bolas" <jmck...@gmail.com> wrote:

The principle advantage of what I'm talking about is that we almost have it already. The wording and so forth for the most complex part has been worked out, and there is viable syntax which already exists. Therefore, the syntactic and standardization burden is minimal.

By contrast, what you're talking about is a completely new construct: arbitrary expression lifetime extension. If that new construct were something that was on the cusp of standardization, I probably wouldn't have made this suggestion. But I don't recall any proposals about generalized expression lifetime extension. I don't even recall any threads on it here.

[...]

Right, but the results of that would be no different from you doing `bool b = expr;`. So there's no "meaningless effect"; you just did the wrong thing.

The principle advantage losses when we
discovered meaningless combinations,
meaningless combinations imply
artificial orthogonality, hence high
degree coupling.

And in principle there is a high degree
coupling: unnamed variable will be
coupled with structured binding, can only
be used at where structured binding
may be used, and has to be extended to
everywhere structured binding being
extended.


Remember: the goal is not to extend the lifetime of `expr`; the goal is to have an unnamed variable.

C++ already has true unnamed object
that is temporary object, and difference
between

  f();
  auto [] = f();

is merely whether the lifetime of the
return value of f() is long enough to
reach the end of the scope.  So why not
call it out?

  __extend_me f();
  T a = g(__extend_me f());

Very straightforward, right?  Attacking
the core of the problem.

--
Zhihao

Nicol Bolas

unread,
Jan 12, 2017, 2:56:55 PM1/12/17
to ISO C++ Standard - Future Proposals, jmck...@gmail.com
On Thursday, January 12, 2017 at 1:56:41 PM UTC-5, Zhihao Yuan wrote:
On Jan 12, 2017 12:29 PM, "Nicol Bolas" <jmck...@gmail.com> wrote:

The principle advantage of what I'm talking about is that we almost have it already. The wording and so forth for the most complex part has been worked out, and there is viable syntax which already exists. Therefore, the syntactic and standardization burden is minimal.

By contrast, what you're talking about is a completely new construct: arbitrary expression lifetime extension. If that new construct were something that was on the cusp of standardization, I probably wouldn't have made this suggestion. But I don't recall any proposals about generalized expression lifetime extension. I don't even recall any threads on it here.

[...]

Right, but the results of that would be no different from you doing `bool b = expr;`. So there's no "meaningless effect"; you just did the wrong thing.

The principle advantage losses when we
discovered meaningless combinations,
meaningless combinations imply
artificial orthogonality, hence high
degree coupling.

... I'm not sure I understand what you're saying. Because it sounds like you're saying that the existence of `bool b = expr` is a problem with the language. Because that's a perfectly valid "meaningless combination". So does that "imply artificial orthogonality, hence high degree coupling?" Where does this "artificial orthogonality" come from, and what is being coupled to what?

Because the only difference between `bool [] = expr` and `bool b = expr` is that you gave it a name. So if one of these is wrong, then the other must be wrong for the same reasons. Accordingly, if one of these is fine, then the other must also be fine.

You cannot be fine with one while claiming that the other is wrong.

And in principle there is a high degree
coupling: unnamed variable will be
coupled with structured binding, can only
be used at where structured binding
may be used, and has to be extended to
everywhere structured binding being
extended.

Right, but I don't care. Using structured binding syntax for this causes no problems which would not already be present when using a named variable in the same circumstances.

Unnamed variable definitions were always going to have some syntax of the form "<typename/placeholder> <syntax to use in lieu of a name> <initializer>".

Remember: the goal is not to extend the lifetime of `expr`; the goal is to have an unnamed variable.

C++ already has true unnamed object
that is temporary object,

No, I said unnamed variable, not unnamed object.
 
and difference
between

  f();
  auto [] = f();

is merely whether the lifetime of the
return value of f() is long enough to
reach the end of the scope.  So why not
call it out?

  __extend_me f();
  T a = g(__extend_me f());

Very straightforward, right?  Attacking
the core of the problem.

That's great. Feel free to write up a proposal for that; it'll have my support.

But that's proposal will not be this. Why? Because I rather suspect that it doesn't let you do this, for example:

for(auto [] : ints(5))
 
//Do this 5 times.

My idea does.

I also don't know how `__extend_me` would impact globals, while I'm fairly sure decomposition declarations can be at global scope. And therefore, global unnamed variables are totally legal.

Can `__extend_me` work with `constexpr` things? Because decomposition declarations can (or if not now, they certainly will once that gets fixed). Can `__extend_me` temporaries have attributes? Because decomposition declarations can. And so forth.

Do you see now why using structured binding here works so well? It already has answered all of the questions that need to be answered. Your idea is in its infancy, and may never cover all the bases that structured binding does. Because yours is solving a different problem.

Declaring a temporary to be `constexpr` makes no sense. Adding attributes to a temporary makes no sense. But applying both to variables does make sense, even if those variables do not have names.

Your idea has merit. But it is ultimately solving a different problem.

However, when you do write up that proposal, you may want to note how it interacts with guaranteed elision. For example, your inner `__extend_me` syntax. That requires that `f()` manifest a temporary, whose lifetime will then be extended. Without that `__extend_me` syntax, a temporary might not be needed. The prvalue returned could directly initialize the function's parameter.

Therefore, using `__extend_me` in various cases can require copy/move constructors.

Zhihao Yuan

unread,
Jan 12, 2017, 3:39:10 PM1/12/17
to std-pr...@isocpp.org, Nicol Bolas
On Jan 12, 2017 1:57 PM, "Nicol Bolas" <jmck...@gmail.com> wrote:
On Thursday, January 12, 2017 at 1:56:41 PM UTC-5, Zhihao Yuan wrote:

Right, but the results of that would be no different from you doing `bool b = expr;`. So there's no "meaningless effect"; you just did the wrong thing.

The principle advantage losses when we
discovered meaningless combinations,
meaningless combinations imply
artificial orthogonality, hence high
degree coupling.

... I'm not sure I understand what you're saying. Because it sounds like you're saying that the existence of `bool b = expr` is a problem with the language. [...]

Because the only difference between `bool [] = expr` and `bool b = expr` is that you gave it a name. So if one of these is wrong, then the other must be wrong for the same reasons. Accordingly, if one of these is fine, then the other must also be fine.

You cannot be fine with one while claiming that the other is wrong.

bool b = expr is right and bool [] = expr
is wrong, because the goal of the former
is to transfer value, the side effect is
a non-goal, while the goal of the later is
RAII, but it lost RAII, so it's wrong.


for(auto [] : ints(5))
 
//Do this 5 times.

My idea does.

Here with or without a name has much
less importance than the RAII cases, but
I can accept it as a valid plus.


I also don't know how `__extend_me` would impact globals, [...]

As usual, end of scope.


Can `__extend_me` work with `constexpr` things? Because decomposition declarations can (or if not now, they certainly will once that gets fixed).

constexpr auto [] = an excellent example
of meaningless combinations because
objects with nontrival destructor does
not work with constexpr.

Can `__extend_me` temporaries have attributes?

Yes if being proven useful.


Do you see now why using structured binding here works so well?

I didn't see, 2 cases down, 1 alive.


Therefore, using `__extend_me` in various cases can require copy/move constructors.

Not in the cases doing your solution's
work though, but thanks for your insight
on this.

--
Zhihao

Zhihao Yuan

unread,
Jan 12, 2017, 5:37:23 PM1/12/17
to std-pr...@isocpp.org, Nicol Bolas
On Thu, Jan 12, 2017 at 1:56 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> Do you see now why using structured binding here works so well? It already
> has answered all of the questions that need to be answered.

One more question: why `auto [] = f();` is different from `f();`?
"Oh, because the former defines a uniquely named variable..."
Well, consider the following thought experiment:

What happens if we redefine the lifetime of the uniquely named
variable `e` in structured binding `auto [a, b, ...] = expr;` as follows:
The lifetime of `e` is no shorter than the full expression where
the last time any name introduced (a, b, ...) being referred to.

So that in

auto [a, b] = f();
g(a);
g2(b);
// e is now destructed
g3();

Sounds like an optimization, but the problem is quite big: one can
take a reference to `a`:

auto& c = a;
// e is destructed

then `c` becomes dangling.

This idea doesn't work because the names in C++ do not work
that way. The lifetime of a name in C++ lasts until the end of
the scope, therefore you expect `a` and `b` are valid until
the end of the scope, and that is why `e` is defined to be a
uniquely named variable, because in that way `e`'s lifetime
will be long enough to support `a` and `b`.

Recall that in the early design papers of structured binding,
there was no such implicit variable, `a` and `b` were individual
copies of the parts from the return value of `f()`. We added
the implicit variable to make `a` and `b` more flexible.
`e` is the result, not the cause.

So what is `auto [] = f();`'s semantics? There is no `a` and
`b`, no name is referring to the return value of f(), so why
the return value cannot be immediately destroyed? Why
the whole thing is different from `f();`?

Nicol Bolas

unread,
Jan 12, 2017, 6:31:09 PM1/12/17
to ISO C++ Standard - Future Proposals, jmck...@gmail.com
On Thursday, January 12, 2017 at 3:39:10 PM UTC-5, Zhihao Yuan wrote:
On Jan 12, 2017 1:57 PM, "Nicol Bolas" <jmck...@gmail.com> wrote:
On Thursday, January 12, 2017 at 1:56:41 PM UTC-5, Zhihao Yuan wrote:

Right, but the results of that would be no different from you doing `bool b = expr;`. So there's no "meaningless effect"; you just did the wrong thing.

The principle advantage losses when we
discovered meaningless combinations,
meaningless combinations imply
artificial orthogonality, hence high
degree coupling.

... I'm not sure I understand what you're saying. Because it sounds like you're saying that the existence of `bool b = expr` is a problem with the language. [...]

Because the only difference between `bool [] = expr` and `bool b = expr` is that you gave it a name. So if one of these is wrong, then the other must be wrong for the same reasons. Accordingly, if one of these is fine, then the other must also be fine.

You cannot be fine with one while claiming that the other is wrong.

bool b = expr is right and bool [] = expr
is wrong, because the goal of the former
is to transfer value,

... I don't understand.

I'm a user. I have an expression <expr>. That expression results in a prvalue of a RAII type that I want to extend over a particular scope of code.

If I type `bool [] = <expr>`, with the above expectation, then I have made a mistake. If I type `bool b = <expr>`, with the same expectation, then I have made the exact same mistake. The only difference is that in the latter case, I introduced a name I don't care about. But the mistake is the same either way: using the wrong type to capture the return of the expression.

Why do you structure your argument such that if I type `bool b = <expr>;` then I clearly wanted "to transfer value", while if I type `bool [] = <expr>;` then I wanted scoping for the object? It sounds like you're assuming your own conclusion, rather than looking at these cases as they are.
 
the side effect is
a non-goal, while the goal of the later is
RAII, but it lost RAII, so it's wrong.

If your goal really is RAII, you wouldn't type `bool []`, since `bool` has no destructor. You keep missing that part of the issue: nobody will type this. They will either use `auto []` or they will use `std::lock_guard []` or whatever the actual type they get from the expression.

You're concerned about someone making a mistake that is highly unlikely to actually happen. I don't see why we should be concerned that someone could type the wrong thing, so long as:

1: They can type the right thing.

2: Typing the right thing requires less effort than the wrong thing.

3: It will be immediately obvious upon reading the code when they've typed the wrong thing.

I've proposed a mechanism that solves the problem it is intended to solve, that is simple to use, that is easily understood, that follows existing constructs in the language, and that requires minimal effort to standardize (in terms of wording).

I don't understand why that is somehow a bad thing. It doesn't even interfere with your `__extend_me` idea, because there are plenty of use cases for that which don't revolve around unnamed variable circumstances.

It's like someone looking at `if constexpr` and saying, "why do we need that, when we have concepts?"

Can `__extend_me` work with `constexpr` things? Because decomposition declarations can (or if not now, they certainly will once that gets fixed).

constexpr auto [] = an excellent example
of meaningless combinations because
objects with nontrival destructor does
not work with constexpr.

I was actually thinking of the side effects of executing the constant expression. You need to store the result in a `constexpr` variable to have greater assurance that the function is executed at compile-time.

But now I realize that it would be difficult to have side-effects if all of the parameters are `constexpr` too. So never mind.

On Thursday, January 12, 2017 at 5:37:23 PM UTC-5, Zhihao Yuan wrote:
On Thu, Jan 12, 2017 at 1:56 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> Do you see now why using structured binding here works so well? It already
> has answered all of the questions that need to be answered.

...

So what is `auto [] = f();`'s semantics?  There is no `a` and
`b`, no name is referring to the return value of f(), so why
the return value cannot be immediately destroyed?  Why
the whole thing is different from `f();`?

Because it looks very much like a variable definition, so nobody will be surprised that it introduces a variable. And most importantly, because the specification says so.

Zhihao Yuan

unread,
Jan 12, 2017, 8:54:46 PM1/12/17
to std-pr...@isocpp.org, Nicol Bolas
On Thu, Jan 12, 2017 at 5:31 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>
> You're concerned about someone making a mistake that is highly unlikely to
> actually happen. I don't see why we should be concerned that someone could
> type the wrong thing, so long as:
>
> 1: They can type the right thing.
>
> 2: Typing the right thing requires less effort than the wrong thing.
>
> 3: It will be immediately obvious upon reading the code when they've typed
> the wrong thing.
>

Yes, real person is not going to write that, but it is still true that
your solution opens a whole can of meaningless thingy.
`bool b = true;` is meaningful, but `bool [] = true;` is meaningless
and `bool [] = f();` is meaningless -- there is no `bool [] = ...;` that
is meaningful.

> I've proposed a mechanism that solves the problem it is intended to solve,
> that is simple to use, that is easily understood, that follows existing
> constructs in the language, and that requires minimal effort to standardize
> (in terms of wording).
>

And I showed that:

1. Coupling your solution to structured binding may be risky, and
2. Designing based on existing specification may be missing the
point of the existing facility.

FrankHB1989

unread,
Jan 12, 2017, 11:36:30 PM1/12/17
to ISO C++ Standard - Future Proposals, jmck...@gmail.com


在 2017年1月13日星期五 UTC+8上午6:37:23,Zhihao Yuan写道:
 
Lifetime is not a property of names.
The lifetime of a static object are not bound with the block scope. The lifetime of reference is (mostly) unspecified.
I probably know what you were meaning, but those misuses of terms are obfuscating.
 

Zhihao Yuan

unread,
Jan 13, 2017, 12:47:07 AM1/13/17
to std-pr...@isocpp.org
On Thu, Jan 12, 2017 at 10:36 PM, FrankHB1989 <frank...@gmail.com> wrote:
>>
>> The lifetime of a name in C++ lasts until the end of
>> the scope, therefore you expect `a` and `b` are valid until
>> the end of the scope, [...]
>
> Lifetime is not a property of names.
> The lifetime of a static object are not bound with the block scope. The
> lifetime of reference is (mostly) unspecified.
> I probably know what you were meaning, but those misuses of terms are
> obfuscating.

Yea, not "lifetime", maybe just "valid until". Thanks.

Nicol Bolas

unread,
Jan 13, 2017, 10:36:11 AM1/13/17
to ISO C++ Standard - Future Proposals, jmck...@gmail.com
On Thursday, January 12, 2017 at 8:54:46 PM UTC-5, Zhihao Yuan wrote:
On Thu, Jan 12, 2017 at 5:31 PM, Nicol Bolas <jmck...@gmail.com> wrote:
>
> You're concerned about someone making a mistake that is highly unlikely to
> actually happen. I don't see why we should be concerned that someone could
> type the wrong thing, so long as:
>
> 1: They can type the right thing.
>
> 2: Typing the right thing requires less effort than the wrong thing.
>
> 3: It will be immediately obvious upon reading the code when they've typed
> the wrong thing.
>

Yes, real person is not going to write that, but it is still true that
your solution opens a whole can of meaningless thingy.
`bool b = true;` is meaningful, but `bool [] = true;` is meaningless
and `bool [] = f();` is meaningless -- there is no `bool [] = ...;` that
is meaningful.

First, let's assume you're right, that `bool [] = ...` is never meaningful.

... so what?

There are other types for which `Typename [] = ...` will be meaningful. You're basically saying we can't have an unnamed variable syntax that includes a specific typename, just because a user could use it with types that aren't meaningful. That makes no sense to me.

Second, what about [[nodiscard]]? If an expression results in a `[[nodiscard]] bool` (perhaps an error code), and you really do choose to disregard it, `bool [] = <expr>` has meaning. It means "Yes, I know this thing told me not to discard this `bool`, but I really do want to discard it."

Third, and most important... decomposition declarations must start with `auto`. They are not allowed to use arbitrary typenames. You might say "yet", but until "yet" becomes "now", all your criticisms in this regard are purely speculative. You're castigating my idea based on syntax that both doesn't exist and hasn't been proposed.

> I've proposed a mechanism that solves the problem it is intended to solve,
> that is simple to use, that is easily understood, that follows existing
> constructs in the language, and that requires minimal effort to standardize
> (in terms of wording).
>

And I showed that:

  1. Coupling your solution to structured binding may be risky

But it doesn't risk anything. The biggest risk you point out is that there are some forms of `typename []` which might have no meaning. Which I remind you, is not a syntax that is at this point legal.

  2. Designing based on existing specification may be missing the
     point of the existing facility.

Yes, we're not using the decomposition syntax for decomposition. So what?

Any form of unnamed variable feature needs to look like a variable declaration. So it's going to be of the form:

<typename/placeholder> <unnamed-syntax> <initializer>

For a user of the language, `[]` seems like a reasonable "unnamed-syntax". `auto [] = ...` looks like a variable declaration. So there is little chance for confusion by the user as to what's going on.

If a couple of minor syntactic and semantic changes allow people to get a useful feature, in a way that doesn't look like a hack, then what does it matter if it is piggy-backing off of a different feature?

To me, it's no different from one of the more recent forms of the `operator-dot` proposal, which suggests using base classes as the means of conversion. That is, your class is not technically derived from that class, but for the purposes of name lookup and conversion, your class looks like it is. Beyond that, all it provides is a user-defined mechanism for doing the derived-to-base instance conversion that had been compiler-defined before.

It is using existing syntactic and semantic infrastructure for a new purpose. Just like my idea here.

Matthew Woehlke

unread,
Jan 13, 2017, 11:23:09 AM1/13/17
to std-pr...@isocpp.org
On 2017-01-12 18:31, Nicol Bolas wrote:
> You're concerned about someone making a mistake that is highly unlikely to
> actually happen. I don't see why we should be concerned that someone could
> type the wrong thing, so long as:
>
> 1: They can type the right thing.
>
> 2: Typing the right thing requires less effort than the wrong thing.
>
> 3: It will be immediately obvious upon reading the code when they've typed
> the wrong thing.

It may not be obvious:

// miraculously compiles, but does the wrong thing
std::lock_guard [] = i_return_a_foo_lock_guard();

...but it wouldn't be obvious if you named the variable either. I agree
that I don't understand what Zhihao Yuan sees as a problem.

Of course, you could argue that you really meant to use `auto` :-).

> I don't understand why that is somehow a bad thing. It doesn't even
> interfere with your `__extend_me` idea, because there are plenty of use
> cases for that which *don't* revolve around unnamed variable circumstances.

...in particular, it would be nice for this to finally work:

for (auto i : std::add_const(__extend_me make_me_a_list())

> On Thursday, January 12, 2017 at 5:37:23 PM UTC-5, Zhihao Yuan wrote:
>> So what is `auto [] = f();`'s semantics? There is no `a` and
>> `b`, no name is referring to the return value of f(), so why
>> the return value cannot be immediately destroyed? Why
>> the whole thing is different from `f();`?
>
> Because it looks very much like a variable definition, so nobody will be
> surprised that it introduces a variable. And most importantly, because the
> specification says so.

In particular, because the specification of S.B. is that it creates a
hidden temporary and then binds aliases to "parts" of that temporary.
Only the second half of that is being omitted in the proposal.

--
Matthew

Matthew Woehlke

unread,
Jan 13, 2017, 11:29:57 AM1/13/17
to std-pr...@isocpp.org
On 2017-01-13 10:36, Nicol Bolas wrote:
> Second, what about [[nodiscard]]? If an expression results in a
> `[[nodiscard]] bool` (perhaps an error code), and you really do choose to
> disregard it, `bool [] = <expr>` has meaning. It means "Yes, I know this
> thing told me not to discard this `bool`, but I really do want to discard
> it."

I was just talking about this in a thread¹ on std-discussion, and I very
much hope assignment to an anonymous variable *will* warn. I don't think
that assignment to anonymous sufficiently indicates intent here. Use
[[maybe_unused]] or a cast-to-void.


https://groups.google.com/a/isocpp.org/d/msg/std-discussion/dE1ubDI3--c/c6GXlO9yCAAJ)

(On a related note, this use of [[maybe_unused]] makes me wonder if we
really ought to have an actual [[unused]], perhaps that warns if the
value is used after all.)

--
Matthew

Matthew Woehlke

unread,
Jan 13, 2017, 12:06:31 PM1/13/17
to std-pr...@isocpp.org
On 2017-01-12 17:37, Zhihao Yuan wrote:
> One more question: why `auto [] = f();` is different from `f();`?
> "Oh, because the former defines a uniquely named variable..."

No; it's different because the return value enters the current scope and
is destroyed at the end of said scope, rather than (as in `f();`) the
end of the statement.

> So what is `auto [] = f();`'s semantics? There is no `a` and
> `b`, no name is referring to the return value of f(), so why
> the return value cannot be immediately destroyed? Why
> the whole thing is different from `f();`?

...because that's not how S.B. is defined? Because having this
difference in semantics is _the whole point_?

This reminds me of a similar point I made in my generalized unpacking paper:

Consider `pt`, a product type of size 2 whose `get<N>(pt)` have side
effects. Now consider `auto x = [1]pt`. The result of this is to
assign `get<1>(pt)` to `auto x`... but what of `get<0>(pt)`; is it
ever called?

If we just write `[1]pt`, it seems obvious that `get<0>(pt)` should not
be called. However, what if we had written `[1][:]pt` instead? What
about `[1]{[:]pt...}`?

This sort of thing is why we allow different phrasings to produce
different side effects; it allows the user to express different intents.
(I propose that only the third calls `get<0>(pt)`. It "has" to, because
`[1]` operates on a temporary object which must first be constructed,
whose construction entails calling `get<0>(pt). Similarly, `auto [] =
f()` specifies the creation of a hidden temporary whose lifetime is the
current scope to receive the value from `f()`. In both cases, that we
subsequently ignore part or all of the values is beside the point; if we
wanted them to be elided entirely, we would have written different code.)

--
Matthew

Zhihao Yuan

unread,
Jan 13, 2017, 1:43:20 PM1/13/17
to std-pr...@isocpp.org
On Fri, Jan 13, 2017 at 11:00 AM, Matthew Woehlke
<mwoehlk...@gmail.com> wrote:
> On 2017-01-12 17:37, Zhihao Yuan wrote:
>> One more question: why `auto [] = f();` is different from `f();`?
>> "Oh, because the former defines a uniquely named variable..."
>
> No; it's different because the return value enters the current scope and
> is destroyed at the end of said scope, rather than (as in `f();`) the
> end of the statement.
>

I asked a design question. It's a 'why', not 'what'. And
your later reply showed that you didn't read my comments
that were removed in your reply.

On Fri, Jan 13, 2017 at 10:23 AM, Matthew Woehlke
<mwoehlk...@gmail.com> wrote:
>
> ...in particular, it would be nice for this to finally work:
>
> for (auto i : std::add_const(__extend_me make_me_a_list())

`add_const` banned rvalue. But in general, yes, things
like this work, because the prvalue is now materialized
on the stack by `__extend_me` (or a real keyword, like
`register`).

Matthew Woehlke

unread,
Jan 13, 2017, 2:17:56 PM1/13/17
to std-pr...@isocpp.org
On 2017-01-13 13:43, Zhihao Yuan wrote:
> On Fri, Jan 13, 2017 at 11:00 AM, Matthew Woehlke wrote:
>> On 2017-01-12 17:37, Zhihao Yuan wrote:
>>> One more question: why `auto [] = f();` is different from `f();`?
>>> "Oh, because the former defines a uniquely named variable..."
>>
>> No; it's different because the return value enters the current scope and
>> is destroyed at the end of said scope, rather than (as in `f();`) the
>> end of the statement.
>
> I asked a design question. It's a 'why', not 'what'. And
> your later reply showed that you didn't read my comments
> that were removed in your reply.

I did read your comments. Maybe I don't understand your point? You
concluded that there is no reason that lifetime extension should happen
here, because the thing being lifetime extended is inaccessible. I
assert that lifetime extension happens anyway, because you wrote
something different whose intent *is lifetime extension*.

See also everything else I wrote in my previous message.

There is no reason why you would ever write `auto [] = f();` instead of
just `f();` except because you *specifically want* the different
semantics of lifetime extending the return value. Just like, in my
alternate example, you would write `[1]{[:]pt...}` instead of `[1]pt`
only because you *specifically want* the different semantics of a call
to `get<0>(pt)` being executed. In both cases, you could argue that
aggressive optimization should make the two equivalent, but also in both
cases there is no sane reason why you would write the second form except
that you *specifically do not* want* such optimization.

IOW, it's different because a) it's useful that way, and b) there is no
reason for it to be the same.

>> ...in particular, it would be nice for this to finally work:
>>
>> for (auto i : std::add_const(__extend_me make_me_a_list())
>
> `add_const` banned rvalue.

...because it is broken without lifetime extension. If we had your
`__extend_me`, that restriction *could* be lifted. (Ideally, we would
want to only lift it if we *know* the lifetime has been extended...
there's something for you to think about...)

--
Matthew

Zhihao Yuan

unread,
Jan 13, 2017, 2:33:53 PM1/13/17
to std-pr...@isocpp.org
On Fri, Jan 13, 2017 at 1:17 PM, Matthew Woehlke
<mwoehlk...@gmail.com> wrote:
> There is no reason why you would ever write `auto [] = f();` instead of
> just `f();` except because you *specifically want* the different
> semantics of lifetime extending the return value. Just like, in my
> alternate example, you would write `[1]{[:]pt...}` instead of `[1]pt`
> only because you *specifically want* the different semantics of a call
> to `get<0>(pt)` being executed.
>

People see things then assign meanings, for good or bad
I don't do that.

Nicol Bolas

unread,
Jan 18, 2017, 9:24:21 PM1/18/17
to ISO C++ Standard - Future Proposals
I have formalized the idea into a more proposal like form.

While composing this, I came to the realization that the `auto []` syntax actually makes more sense than I thought. For example, consider one of the oft-requested features of decompositions: not specifying a name.

Well, you could (in theory; the proposal doesn't include this) allow `[]` to be used to not specify a name. And it would work naturally:

auto [first, [], third] = expr;

Users would come to see `[]` as meaning "unnamed" or "unspecified".

It would also work out naturally if we expand decompositions into multiple levels:

auto [ [], [inner_first, inner_second], third ] = expr;

So the syntax is not really as much of a hack as I thought.
Unnamed Variables.html

Thomas Köppe

unread,
Jan 19, 2017, 12:25:00 PM1/19/17
to ISO C++ Standard - Future Proposals
On Thursday, 19 January 2017 02:24:21 UTC, Nicol Bolas wrote:
It would also work out naturally if we expand decompositions into multiple levels:

auto [ [], [inner_first, inner_second], third ] = expr;

So the syntax is not really as much of a hack as I thought.

 Isn't that an ill-formed attribute-specifier-seq? You say it works out naturally; have you implemented this?

Nicol Bolas

unread,
Jan 19, 2017, 1:11:16 PM1/19/17
to ISO C++ Standard - Future Proposals

It works out naturally visually; I have no idea how it works out grammatically. However, any grammatical issues would have to be solved if we're going to have multi-level decompositions of any kind. So any such parsing issues are not specifically the fault of using `[]` to be a placeholder for an unspecified name.

Also, I'm not proposing that. My main point in bringing it up is that, if you add multi-level decomposition, then you can essentially get `[]` to be a placeholder for an unspecified name. And therefore using `[]` to mean "unnamed" doesn't seem as arbitrary.

Matthew Woehlke

unread,
Jan 19, 2017, 1:50:30 PM1/19/17
to std-pr...@isocpp.org
On 2017-01-19 13:11, Nicol Bolas wrote:
> My main point in bringing it up is that, if you add multi-level
> decomposition, then you can essentially get `[]` to be a placeholder
> for an unspecified name. And therefore using `[]` to mean "unnamed"
> doesn't seem as arbitrary.

Actually, I don't think I agree with that logic. Let's say, instead,
that we used '_' for anonymous variables:

auto _ = foo();
auto [_, [ia, ib], c] = bar();

This proceeds just as naturally.

That's not to say that I disagree with the logic of implementing
anonymous variables by redefining `auto []` to mean "a decomposition
declaration where I don't actually want to bind anything", nor that
doing so logically extends that feature to ignoring some values if we
subsequently get nested decomposition. I just don't agree with the
converse; that this feature logically falls out of nested decomposition.

Also, while your proposal strengthens a case for nested decomposition,
because being able to ignore a value in a decomposition - which is
something folks seem to want - naturally falls out of combining the
features, that would (as shown above) be true regardless of the syntax.

If anything, this might be an argument *against* your proposed syntax,
because using nested []'s for nested decomposition has the potential of
being grammatically ambiguous (as noted). If we do get nested
decomposition, but it looks like e.g. `auto [{a, b}, c] = ...`, then we
lose the ability to combine the features.

--
Matthew

Peter Koch Larsen

unread,
Jan 19, 2017, 5:51:36 PM1/19/17
to std-pr...@isocpp.org
But you can't use _ to mean an anonymous variable. _ is already a
valid name and used by some for various purposes.

/Peter
> --
> 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.
> 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/58810A73.6030003%40gmail.com.

Vicente J. Botet Escriba

unread,
Jan 19, 2017, 6:04:42 PM1/19/17
to std-pr...@isocpp.org
Le 19/01/2017 à 23:51, Peter Koch Larsen a écrit :
> But you can't use _ to mean an anonymous variable. _ is already a
> valid name and used by some for various purposes.
>
Maybe it is no to late to say that _ is not a valid name when used on a
structure binding context.
In this way we could use it later to mean an name that is ignored.

Of course this wouldn't help us to have unnamed variables yet.

Vicente

Vicente J. Botet Escriba

unread,
Jan 19, 2017, 6:10:44 PM1/19/17
to std-pr...@isocpp.org
I know it is a extreme case, but wondering if matching  tuple<> would be a case to consider.

    auto [] = tuple<>{};

if [a, b, c] is used to match a product type of size 3, it seems natural that [] would be used to match a product type of size 0.


Vicente

Nicol Bolas

unread,
Jan 19, 2017, 6:11:16 PM1/19/17
to ISO C++ Standard - Future Proposals
On Thursday, January 19, 2017 at 5:51:36 PM UTC-5, Peter Koch Larsen wrote:
But you can't use _ to mean an anonymous variable. _ is already a
valid name and used by some for various purposes.

He was proposing a hypothetical to make a point. Nothing more.

Peter Koch Larsen

unread,
Jan 19, 2017, 6:11:30 PM1/19/17
to std-pr...@isocpp.org
> --
> 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.
> 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/1ce02dde-d0c5-37ea-1219-e5e97fd329ee%40wanadoo.fr.

Peter Koch Larsen

unread,
Jan 19, 2017, 6:15:51 PM1/19/17
to std-pr...@isocpp.org
On Fri, Jan 20, 2017 at 12:04 AM, Vicente J. Botet Escriba
<vicent...@wanadoo.fr> wrote:
Perhaps not. But I believe that using _ for an unnamed variable is
more confusing than Nicols proposal.
After some reflection, I actually consider Nicols proposal as not
confusing at all provided you need anonymous variables in the first
place.

/Peter

Bengt Gustafsson

unread,
Jan 19, 2017, 6:31:04 PM1/19/17
to ISO C++ Standard - Future Proposals
Maybe the square bracket is starting to get too overloaded, we also have the prefix [] to create pack slices in another proposal.

In the context of structured binding such an unnamed variable would always be the only token between two commas so it seems that * or ? could be used to mimic glob patterns, and it might even be useful to define that ? means one and * means any number of. For instance this would allow picking up the two first elements of an arbirary tuple into two separate variables while ignoring the rest:

auto [ int a, int b, * ] = my_tuple;

Or to just pick up the second member of a pair:

auto [ ?, int s ] = my_pair;

I think this would carry over nicely to a future nested-decomposition proposal.

Nicol Bolas

unread,
Jan 19, 2017, 8:07:51 PM1/19/17
to ISO C++ Standard - Future Proposals

I don't see how that would cause a problem, since it shouldn't be checking `tuple_size` at all.
 

Derek Ross

unread,
Jan 19, 2017, 11:48:10 PM1/19/17
to ISO C++ Standard - Future Proposals
Maybe some combination of keywords can be used for anonymous variables. "auto auto = foo()" sorta makes sense... an automatic type followed by an automatic name. Or maybe "auto default = foo()".

Nicol Bolas

unread,
Jan 20, 2017, 12:37:51 AM1/20/17
to ISO C++ Standard - Future Proposals
On Thursday, January 19, 2017 at 11:48:10 PM UTC-5, Derek Ross wrote:
Maybe some combination of keywords can be used for anonymous variables. "auto auto = foo()" sorta makes sense... an automatic type followed by an automatic name. Or maybe "auto default = foo()".

The whole point of my idea, the reason I choose that syntax, is that it's piggybacking off of decomposition declarations. Making it work, in terms of standard changes, requires a minimum of effort: two minor grammar tweaks and modifying at most 4 paragraphs.

Using a different syntax, whatever that may be, loses that advantage.

Domen Vrankar

unread,
Jan 20, 2017, 2:59:25 AM1/20/17
to std-pr...@isocpp.org
+1 with difference that I liked it from the start and liked it even more with the future suggestions part added. It looks and feels consistent and also [] is far more visible than the usual suggested underscore. In my mind it's marked as variable capture without the possibility to access it since the name was not specified - more or less anonymous variable but thought of as capture group so the syntax really fits the purpose.

I also have a possibly terrible idea for future extension of the idea. Usual proposal for skipping the captured variables is a wildcard - I've usually seen proposals of ? or _ for skipping one and ... for skipping the rest of variables.

Let's say we have function foo() returning a tuple of 5 elements and we don't care about the first three:
auto [?, ?, ?, int x, float y] = foo();

alternative with this proposal could be:

auto [[3], int x, float y] = foo(); // now the brackets really shine compared to alternative single character suggestions

Skipping the rest could either still be ... or [2] where the number represents the rest of the elements (the control freak version of skipping the rest ;).

Anyhow for me the brackets are consistent, visually appealing and also extendable more than the rest of the alternative suggestions that I've heard of.

Regards,
Domen

Domen Vrankar

unread,
Jan 20, 2017, 3:45:47 AM1/20/17
to std-pr...@isocpp.org
Hm just thought of one extra future extension on top of that - just a random not thought through idea... (haven't even checked if array syntax could cause issues in parameter pack section).

(Ab)using structured binding and unnamed variables for array/tuple/string slicing:

auto [x[3], y[2]] = foo(); // get two slices
auto [x[3], [2]] = foo(); // ignore the rest
auto [[2], x[2], []] = foo(); // get the middle
auto [[3], x[2], ...] = foo(); // get some elements from a large template parameter pack of which we don't know the length
auto [[3], x[...]] = foo(); // skip some elements from a large template parameter pack and create a tuple from the rest

IMHO extensibility and visual consistency of [] as unnamed variables proposal looks promising.

Regards,
Domen

Domen Vrankar

unread,
Jan 20, 2017, 3:53:38 AM1/20/17
to std-pr...@isocpp.org
Ups... For these two:
auto [[3], x[2], ...] = foo(); // get some elements from a large template parameter pack of which we don't know the length
auto [[3], x[...]] = foo(); // skip some elements from a large template parameter pack and create a tuple from the rest

I meant:

auto [[3], x[2], ...] = args...;
auto [[3], x[...]] = args...;

Too much copy pasting :D

Regards,
Domen

inkwizyt...@gmail.com

unread,
Jan 20, 2017, 12:19:47 PM1/20/17
to ISO C++ Standard - Future Proposals
 
Small detail, as `[[` start attribute maybe we need prefix nested one by `auto`?

auto[ auto [], b, auto [c, d]] = foo();

Matthew Woehlke

unread,
Jan 20, 2017, 2:52:09 PM1/20/17
to std-pr...@isocpp.org
On 2017-01-20 03:45, Domen Vrankar wrote:
> 2017-01-20 8:58 GMT+01:00 Domen Vrankar <domen....@gmail.com>:
>> I also have a possibly terrible idea for future extension of the idea.
>> Usual proposal for skipping the captured variables is a wildcard - I've
>> usually seen proposals of ? or _ for skipping one and ... for skipping the
>> rest of variables.
>>
>> Let's say we have function foo() returning a tuple of 5 elements and we
>> don't care about the first three:
>> auto [?, ?, ?, int x, float y] = foo();

I'd really rather see `auto [x, y] = [3:]foo();` for this sort of thing,
at least as opposed to extending the "placeholder" syntax past the
original proposal.

> Hm just thought of one extra future extension on top of that - just a
> random not thought through idea... (haven't even checked if array syntax
> could cause issues in parameter pack section).
>
> (Ab)using structured binding and unnamed variables for array/tuple/string
> slicing:
>
> auto [x[3], y[2]] = foo(); // get two slices

Just... no... Leaving aside my opinion on the aesthetics of that, what
is the type of `x` or `y`? Do you just enforce that it is `std::tuple`
always? What if `foo()` returns a `std::array`?

This is why P0535¹ specifies that you get a *parameter pack*. That way,
if you want the values reassembled into another product type, you get to
choose which one you want.

I also don't think DD should be abused like this to create a product
type from a slice of another product type. You lose the "decomposition"
part doing that...

(¹ Not published yet, but see
https://github.com/mwoehlke/cpp-proposals/blob/master/p0535r0-generalized-unpacking.rst)

--
Matthew

Matthew Woehlke

unread,
Jan 20, 2017, 2:53:38 PM1/20/17
to std-pr...@isocpp.org
On 2017-01-19 18:31, Bengt Gustafsson wrote:
> Maybe the square bracket is starting to get too overloaded, we also have
> the prefix [] to create pack slices in another proposal.

Fortunately, that does not interfere in this case; you have already seen
'auto', so unpacking/slicing cannot appear next.

> In the context of structured binding such an unnamed variable would always
> be the only token between two commas so it seems that * or ? could be used
> to mimic glob patterns, and it might even be useful to define that ? means
> one and * means any number of. For instance this would allow picking up the
> two first elements of an arbirary tuple into two separate variables while
> ignoring the rest:
>
> auto [ int a, int b, * ] = my_tuple;
>
> Or to just pick up the second member of a pair:
>
> auto [ ?, int s ] = my_pair;

...or `auto s = [1]my_pair;` :-).

The down side is that this is much harder to specify than the original
proposal. I *do* like the original proposal for that reason. If it could
be non-ambiguous with attributes, that would be ideal.

S.B. was originally going to use a syntax other than []s... I'm starting
to wonder if maybe we should have stuck to that...

Actually... I think it *is* non-ambiguous, but requires (possibly
significant) look-ahead, which compiler authors never like. An attribute
is always of the form `[[stuff]]` (plus optional whitespace), where I
believe `stuff` never contains an unquoted `]` or `[`, whereas an
ambiguous nested DD is of the form `[[...],...]`; that is, there will
always occur at least a `,` between the inner and outer closing `]`s.
(We could require e.g. `[[a],]` for the degenerate - and probably very,
very rare - case of binding `a` to `get<0>(get<0>(rhs))`.)

--
Matthew

Bengt Gustafsson

unread,
Jan 20, 2017, 6:18:38 PM1/20/17
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
Its not only about compilers being able to interpret (!) the source code, it is more about humans being able to do so (and write it correctly). The rule that one construct should do the same or at least similar things is a good thing, so as square brackets are already used for indexing, attributes and structured binding whereas, with pack slicing as runner up ? is only an infix operator I thought it could be a better choice for the unnamed variable use. You could throw in a "step over defaulted parameter" use as it has similar semantics (at least superficially).

Does the current definition of structured binding always allow trailing elements in the rhs to be ignored? If so the use of * or ... to mean the rest seems less important. Picking the first and last elements is a possible use case but this is probably very seldom useful in practice.

Matthew Woehlke

unread,
Jan 20, 2017, 6:40:01 PM1/20/17
to std-pr...@isocpp.org
On 2017-01-20 18:18, Bengt Gustafsson wrote:
> ? is only an infix operator I thought it could be a better choice for
> the unnamed variable use.

I could live with it... we might then write:

auto ?= get_raii(); // space between '?' and '=' intentionally omitted

I think it's interesting to present the proposal in its current form,
but possibly we could extend DD to allow `?` instead of the
identifier-list (in square brackets) if folks strongly prefer that syntax.

> You could throw in a "step over defaulted parameter" use as
> it has similar semantics (at least superficially).

I think I'd prefer to either seem support for specifying named arguments
when calling a function, or use `default` for that purpose.

> Does the current definition of structured binding always allow trailing
> elements in the rhs to be ignored?

No; the number of names being bound must match the tuple-size of the RHS.

> Picking the first and last elements is a
> possible use case but this is probably very seldom useful in practice.

I'd rather have D0535 for that :-).

--
Matthew

Bengt Gustafsson

unread,
Jan 21, 2017, 4:10:39 AM1/21/17
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
Maybe we are talking about different things, I was refering to the suggested use of nested empty brackets to skip binding individual elements in a structured binding assignment so your raii example would be:

auto [?] = get_raii();

or if get_raii can return any number of things:

auto [*] = get_raii();

or maybe:

auto [...] = get_raii();


But now that you put it forth it does seem logical to allow your case too if get_raii returns a single value thus separating the ideas of using ? as a unnamed variable from the structured binding concept, the ? becomes just a reusable anonymous name, which is something that awkward and often unsafe macros have been used for so far.


At this point and given the fact that the element counts in structured binding must match my interpretation of the [] suggestion as a degenerate case of nested structured binding does not work, and it seems very hard to explain this difference:

auto [] = get_raii();    // Fails as get_raii does not return 0 values.

auto [ [] ] = get_raii()   // Succeeds as the inner empty brace goes by some other rule?

So my understanding must be that as no function can return no values the [] as such is not usable in structured binding so we can grab that syntax for this other purpose. Then I must refer back to my initial complaint about excessive overloading of the square bracket.

Can you produce a link to D0535?

Nicol Bolas

unread,
Jan 21, 2017, 9:13:05 AM1/21/17
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
On Saturday, January 21, 2017 at 4:10:39 AM UTC-5, Bengt Gustafsson wrote:
Maybe we are talking about different things, I was refering to the suggested use of nested empty brackets to skip binding individual elements in a structured binding assignment so your raii example would be:

auto [?] = get_raii();

or if get_raii can return any number of things:

auto [*] = get_raii();

or maybe:

auto [...] = get_raii();


But now that you put it forth it does seem logical to allow your case too if get_raii returns a single value thus separating the ideas of using ? as a unnamed variable from the structured binding concept, the ? becomes just a reusable anonymous name, which is something that awkward and often unsafe macros have been used for so far.


At this point and given the fact that the element counts in structured binding must match my interpretation of the [] suggestion as a degenerate case of nested structured binding does not work, and it seems very hard to explain this difference:

auto [] = get_raii();    // Fails as get_raii does not return 0 values.

auto [ [] ] = get_raii()   // Succeeds as the inner empty brace goes by some other rule?

But the first one isn't supposed to fail. My point in bringing up this possibility (and again, I'm not proposing it, I'm just showing it as a possible future direction) is to show that the idea isn't as crazy as it sounds.

By doing what I've suggested, `[]` effectively means "there's a variable with no name". So if you do `auto [] = ...`, then you create a variable with no name from the expression. If you do `auto [ [] ] = ...`, then you create a variable with no name from the expression, then decompose it into one member which has no name.

In so doing, the meaning of `[]` is somewhat normalized.

Bengt Gustafsson

unread,
Jan 22, 2017, 5:13:05 PM1/22/17
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
Going back to your original proposal it seems that you want to use a rule of implicitly throwing away excessive RHS elements, in this case the one and only.

But according to Matthew Woehlke a few posts up the number of elements on the LHS and RHS of a structured binding assignment must match.

So it seems that if this rule was changed your proposal would be a natural consequence, but with the rule as stated I don't find in particularly logical, and would prefer the ? syntax as I suggested, with the more general interpretation of introducing a unnamed variable, (which has little to do with structural binding). You can then write:

unique_lock ?(mMutex);

which I guess would be a common pattern, and just as if ? was a real identifier identical with:

auto ? = unique_lock(mMutex);


You could also write:

void func(int ?) override { do_something; }

To emphatize that you are not using this parameter in this override. 




Nicol Bolas

unread,
Jan 22, 2017, 7:34:12 PM1/22/17
to ISO C++ Standard - Future Proposals, mwoehlk...@gmail.com
On Sunday, January 22, 2017 at 5:13:05 PM UTC-5, Bengt Gustafsson wrote:
Going back to your original proposal it seems that you want to use a rule of implicitly throwing away excessive RHS elements, in this case the one and only.

But according to Matthew Woehlke a few posts up the number of elements on the LHS and RHS of a structured binding assignment must match.

Yes that's normally true, and I mentioned that in the proposal. It's item #2 in the changes to the standard that would be needed for this to work.

So it seems that if this rule was changed your proposal would be a natural consequence, but with the rule as stated I don't find in particularly logical,

I find it perfectly logical from a decomposition standpoint. `auto []` doesn't perform decomposition. So it doesn't matter if the expression could be decomposed, since you didn't ask for it to be decomposed.

Magnus Fromreide

unread,
Jan 23, 2017, 3:47:15 AM1/23/17
to std-pr...@isocpp.org, mwoehlk...@gmail.com
This does actually suggest another spelling of the anonymous variable, namely
nothing at all,

auto = unique_lock...
void func(int) { ... }

Now, I will admit to not having thought much about this at all but using
nothing would be in line with how you declare unnamed function arguments.

One con of this idea is obviously that the distance from

auto someting = ...

to

auto = ...

is smaller than the distance to

auto ? = ...

or

auto [] = ...

/MF

Matthew Woehlke

unread,
Jan 23, 2017, 11:50:29 AM1/23/17
to std-pr...@isocpp.org
On 2017-01-21 04:10, Bengt Gustafsson wrote:
> Maybe we are talking about different things, I was refering to the
> suggested use of nested empty brackets to skip binding individual elements
> in a structured binding assignment so your raii example would be:
>
> auto [?] = get_raii();
>
> or if get_raii can return any number of things:
>
> auto [*] = get_raii();
>
> or maybe:
>
> auto [...] = get_raii();
>
> But now that you put it forth it does seem logical to allow your case too
> if get_raii returns a single value thus separating the ideas of using ? as
> a unnamed variable from the structured binding concept, the ? becomes just
> a reusable anonymous name, which is something that awkward and often unsafe
> macros have been used for so far.

Another way of looking at this that might work: if the identifier-list
of a decomposition declaration is `?`, then *at that level*, no attempt
to decompose the value is made (i.e. the original proposal). Note that
this explicitly permits that the value is not a product type, e.g.:

int foo();
auto [?] = foo(); // okay

This would naturally extend to nested decompositions, regardless of how
those end up being formatted.

> At this point and given the fact that the element counts in structured
> binding must match my interpretation of the [] suggestion as a degenerate
> case of nested structured binding does not work, and it seems very hard to
> explain this difference:
>
> auto [] = get_raii(); // Fails as get_raii does not return 0 values.
>
> auto [ [] ] = get_raii() // Succeeds as the inner empty brace goes by
> some other rule?

If an empty identifier-list is used for anonymous binding, the first
matches anything at all (no checks performed), while the second
(assuming that is a nested decomposition syntax) would match a product
type of size 1.

I have to admit, this is making me inclined to like `[?]` as the syntax.
This still requires minimal change (a small grammar addition to
identifier-list for DD's, plus the original proposed change), and has
much the same benefits for nested decomposition, but dodges the question
of syntax for nested decomposition. For example:

auto [<a, b>, <c, d>, e] = pt(); // let's say NDD looks like this
auto [<?>, <c, d>, <?>] = pt(); // like above, but only bind c, d
auto [<a, <?>>, <c, d>, <?>] = pt(); // ...or bind a, c, d

...so in effect there is a superfluous set of DD brackets around an
unnamed binding site, but it allows us to achieve the feature with less
grammar change.

Of course, this also suggests that an empty identifier-list could mean
the same thing:

auto [<>, <c, d>, <>] = pt(); // same as above
auto [<a, <>>, <c, d>, <>] = pt(); // same as above

...so we're not talking about literal `[]` as the placeholder, we're
talking about a DD (nested or otherwise) with an empty identifier-list.
Looked at that way, my original concern of this not scaling to nested
decompositions is moot.

> So my understanding must be that as no function can return no values

`void`? `struct {}`?

I can also trivially make tuple_size<T>() be 0 for some user type.

That said...

> the [] as such is not usable in structured binding so we can grab
> that syntax for
> this other purpose. Then I must refer back to my initial complaint
> about excessive overloading of the square bracket.

...I don't see overriding `[]` for this purpose to be a problem; you can
still use it to bind to a 0-tuple, since it binds to anything at all,
and the only expressiveness you lose is the assertion that what you are
binding to really is a 0-tuple. But those are likely rare anyway.

> Can you produce a link to D0535?

https://github.com/mwoehlke/cpp-proposals/blob/master/p0535r0-generalized-unpacking.rst
(it will be in the next mailing).

--
Matthew
Reply all
Reply to author
Forward
0 new messages