if constexpr (constexpr auto[state, value] = ...; state) {}
3.2 Should this syntax support initialization from a braced-init-list?
For example:
auto {x,y,z} = {1, “xyzzy”s, 3.14159}; // NOT proposed We think the answer should be no.
This would be trivial to add, but should be well motivated and we know of no use cases where this offers additional expressive power not already available (and with greater clarity) using individual variable declarations."
for (auto[x, y, z] = {1, "xyzzy"s, 3.14159}; ...; ...) {}
for (auto[x, y, z] = std::tuple{1, "xyzzy"s, 3.14159}; ...; ...) {}
auto[x...] = something;
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
auto&&[values...] = std::forward<T>(t);
return std::invoke(std::forward<F>(f), values...);
}
template <class... Args> void print(Args... args);
auto[values...] = func(...);
print(values...);
//uses std::make_index_sequence
template <size_t End> constexpr auto make_index_sequence_array() -> std::array<size_t, End>;
template <size_t From, size_t To, class Tuple>
constexpr auto select_tuple_part(Tuple&& tuple)
{
using T = std::decay_t<Tuple>;
static_assert(From < To && To < std::tuple_size_v<T> && From < std::tuple_size_v<T>);
constexpr auto[I...] = make_index_sequence_array<To - From>();
return std::tuple{std::get<(I+From)...>(tuple)};
}
template <class Tuple>
constexpr auto reverse_tuple(Tuple&& tuple)
{
using T = std::decay_t<Tuple>;
constexpr size_t len = std::tuple_size_v<T>;
if constexpr (len == 0)
return std::tuple{};
else
{
constexpr auto[I...] = make_index_sequence_array<len>();
return std::tuple{std::get<len - I - 1>(tuple)...};
}
}
template<auto[values....]> struct A;
template <class CharT, CharT... Chars> constexpr auto operator""_s() -> std::array<CharT, sizeof...(Chars)>;
A<"Hello"_s> tmp;
Hi,So I've been reading the past discussions surrounding structured binding.It is my understanding that due to time constraints the proposal was really bare bone and that some possible improvements should be checked in the future.This got me quite inspired and I'm waiting for some feedback.Disclaimer: Most of them depends on constexpr structured binding.I. Declarator specifiers for structured binding (useful, desirable)allowing constexpr, static, thread_local specifiers.
if constexpr (constexpr auto[state, value] = ...; state) {}II. initialization from a braced-init-list (trivial, one use case)While reading the old P0144R0 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf) the section 3.2 caught my attention.
3.2 Should this syntax support initialization from a braced-init-list?
For example:
auto {x,y,z} = {1, “xyzzy”s, 3.14159}; // NOT proposed We think the answer should be no.
This would be trivial to add, but should be well motivated and we know of no use cases where this offers additional expressive power not already available (and with greater clarity) using individual variable declarations."I think I've found a use case. Not a big one, but still
for (auto[x, y, z] = {1, "xyzzy"s, 3.14159}; ...; ...) {}Which would be the same as:
for (auto[x, y, z] = std::tuple{1, "xyzzy"s, 3.14159}; ...; ...) {}But without having to use std::tuple.
III. Parameter pack through structured binding (controversial?, useful, complex to implement?)
Second, if we're going to allow this, then it should not involve `std::tuple`. We would simply declare that a structured binding statement initialized with a braced-init-list simply creates multiple variables with types deduced from the appropriate locations in the braced-init-list. We should not needlessly associate a language feature like this with a library type.
for (auto[x, y, z] = std::tuple{1, "xyzzy"s, 3.14159}; ...; ...) {}
This does not:
No, we have other proposals dealing with packs.
Le vendredi 21 juillet 2017 02:20:00 UTC+2, Nicol Bolas a écrit :
Second, if we're going to allow this, then it should not involve `std::tuple`. We would simply declare that a structured binding statement initialized with a braced-init-list simply creates multiple variables with types deduced from the appropriate locations in the braced-init-list. We should not needlessly associate a language feature like this with a library type.
This compiles, now. It's valid C++.
for (auto[x, y, z] = std::tuple{1, "xyzzy"s, 3.14159}; ...; ...) {}
This does not:
for (auto[x, y, z] = {1, "xyzzy"s, 3.14159}; ...; ...) {}
Le vendredi 21 juillet 2017 02:20:00 UTC+2, Nicol Bolas a écrit :No, we have other proposals dealing with packs.The last one to have a similar impact on the language was P0341R0 in 2016 and was not well received (https://groups.google.com/a/isocpp.org/forum/#!topic/reflection/KWotU1R0UCI),
auto [] = acquire_lock(); //not really self evident
auto = acquire_lock();//simpler
int fd = ...;
scope_exit [] = []() { close(fd); };
...
func_that_may_throw();
...
auto [a, void, c] = some_3tuple(); // don't care about second part
What about overloading the `void` keyword for this purpose? IMO it makes the intent quite a bit more obvious than empty square brackets do.
auto [a, void, c] = some_3tuple(); // don't care about second part
-Ray
On Monday, July 31, 2017 at 11:20:15 AM UTC-4, Matthew Woehlke wrote:On 2017-07-28 18:22, Daemon Snake wrote:
> As for anonymous variables I'm not sure that structured binding would be
> the best way to achieve it.
> auto [] = acquire_lock(); //not really self evident
> auto = acquire_lock();//simpler
I understand that, but I believe that both have been suggested and
neither has gained traction. The latter has possible grammar issues that
the former can dodge. Also, the former, assuming we get nesting, will
also allow things like:
auto [a, [], b] = some_3tuple(); // don't care about second part
...which is something else that seems wanted.
> I'm not quite sure the kind of syntax we would need to use though.
> scope_exit [] = []() { close(fd); };
auto [] = scope_exit{[]() { ... }};
The type doesn't need to be on the LHS.
--
Matthew
--
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/6592c3d5-9227-4964-9580-9a922b6f7c60%40isocpp.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-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/CAFk2RUbXLjVw19GHP4E3f9wBOkz-RJaYu%2BxmXDk_Xrkibrv00Q%40mail.gmail.com.
We can make _ work, and be backwards compatible with current usage:
some_type _;
void func()
{
int _ = ...; //is now anonymous by side effect
}
//making name optional for C-style struct/class variable declaration only if initialized
//anonymous variables are only useful for struct/class types anyway
class type_name = ...; //declares an anonymous variable of type type_name
class auto = ...; //auto is a reserved keyword so why not
//Impact on the syntax ?
class double =...; //no changes, illegal double is not a struct/class
class type_name; //no changes, legal, declares a struct named t
class type_name id; //no changes, legal.
class type_name id = ...; //no changes, legal.
On 1 August 2017 at 03:37, Tony V E <tvan...@gmail.com> wrote:
> We can make _ work, and be backwards compatible with current usage:
>
> auto [a, _, c] = some_3tuple(); // don't care about second part
>
> int x = _; // ok, whatever, I guess it is a normal variable (backwards
> compat!)
>
> lock_guard _ = ...; // sure reuse _ - it is now anonymous
How is that lock_guard definition backwards compatible?
> auto copy = _; // error: _ because anonymous, but was reused
How is that backwards compatible?
Le mardi 1 août 2017 02:37:51 UTC+2, Tony V E a écrit :We can make _ work, and be backwards compatible with current usage:That could be quite dangerous as '_' is a legal C++ identifier.One of the focus of the standard is still to maintain legacy code.Imagine this:
some_type _;
void func()
{
int _ = ...; //is now anonymous by side effect
}
What about using __ instead. This is more
constrained as used only by the standard library. The compiler
should signal an error when used in other contexts.
Vicente
On 1 August 2017 at 14:28, Tony V E <tvan...@gmail.com> wrote:
>> > We can make _ work, and be backwards compatible with current usage:
>> >
>> > auto [a, _, c] = some_3tuple(); // don't care about second part
>> >
>> > int x = _; // ok, whatever, I guess it is a normal variable (backwards
>> > compat!)
>> >
>> > lock_guard _ = ...; // sure reuse _ - it is now anonymous
>>
>> How is that lock_guard definition backwards compatible?
>
>
> Currently reusing a variable name in the same scope is an error, so I'm
> saying - for _ only - don't make it an error. Make it mean "_ is now
> anonymous for the rest of this scope"
Woo hoo, context-specific semantics that silently change depending on
prior declarations
or lack of them in the same scope. NO.
>> > auto copy = _; // error: _ because anonymous, but was reused
>>
>> How is that backwards compatible?
>>
>
> You currently can't have 2 _ variables in the same scope. That last line is
> in the context of "doesn't compile today".
auto _ = whatever;
auto copy = _;
compiles fine, today.
I don't like
auto [] = foo();
because that might lead to
auto [x,y] = Point3D(1,2,3); // drop Z
and then you lose the chance of catching an error there.
auto [x, y, z] = some_func(...);
vs
auto [x, y] = some_func(...);
On 08/04/2017 01:22 PM, Nicol Bolas wrote:
On Friday, August 4, 2017 at 12:56:33 PM UTC-4, Tom Honermann wrote:On 08/03/2017 09:21 PM, Tony V E wrote:
I don't like
auto [] = foo();
because that might lead to
auto [x,y] = Point3D(1,2,3); // drop Z
and then you lose the chance of catching an error there.
I've heard this argument before, but I don't really understand it. If the user needs the undeclared binding, I would expect them to pretty quickly notice its absence. If the undeclared binding isn't needed, then why should the user be forced to name it? I appreciate that, depending on how the feature is defined, there could be an observable difference when binding tuple-like types if calls to get<>() were elided for undeclared bindings (assuming that the call to get<>() had side effects), but such eliding seems like it would be desirable in the general case.
What if the user actually meant to call something different?
auto [x, y, z] = some_func(...);
vs
auto [x, y] = some_func(...);
Whether the user uses `z` is not relevant. What matters most is that the user called a function that returns 3 values. If the latter compiles, then there is an inconsistency between what the user specified (2 values) and what the API specified (3 values).
Such inconsistencies are usually considered compile errors. They certainly are when you call a function that takes 3 parameters, but you only provide 2. Why should return values be any different?
Well, it isn't an error to call a function that takes 3 arguments when only 2 are provided if the function declaration supplies a default argument for the 3rd.
I don't find this argument compelling. There is only one return type and the return type does not influence overload resolution. To me, this sounds like an argument against use of the 'auto' type specifier at all -
what if the user meant to call a function returning some class type instead of std::tuple?
The ability to omit declarations for unwanted bindings seems more useful to me than the opportunity to catch cases where an unintended function was called.
--
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/d5c8a5ba-72f3-1f6a-2c93-aa0bffdacfa3%40wanadoo.fr.
On Friday, August 4, 2017 at 6:04:05 PM UTC-4, Tom Honermann wrote:On 08/04/2017 01:22 PM, Nicol Bolas wrote:
On Friday, August 4, 2017 at 12:56:33 PM UTC-4, Tom Honermann wrote:On 08/03/2017 09:21 PM, Tony V E wrote:
I don't like
auto [] = foo();
because that might lead to
auto [x,y] = Point3D(1,2,3); // drop Z
and then you lose the chance of catching an error there.
I've heard this argument before, but I don't really understand it. If the user needs the undeclared binding, I would expect them to pretty quickly notice its absence. If the undeclared binding isn't needed, then why should the user be forced to name it? I appreciate that, depending on how the feature is defined, there could be an observable difference when binding tuple-like types if calls to get<>() were elided for undeclared bindings (assuming that the call to get<>() had side effects), but such eliding seems like it would be desirable in the general case.
What if the user actually meant to call something different?
auto [x, y, z] = some_func(...);
vs
auto [x, y] = some_func(...);
Whether the user uses `z` is not relevant. What matters most is that the user called a function that returns 3 values. If the latter compiles, then there is an inconsistency between what the user specified (2 values) and what the API specified (3 values).
Such inconsistencies are usually considered compile errors. They certainly are when you call a function that takes 3 parameters, but you only provide 2. Why should return values be any different?
Well, it isn't an error to call a function that takes 3 arguments when only 2 are provided if the function declaration supplies a default argument for the 3rd.
Which requires explicit syntax to be used on the function declaration. By your logic, to have a function return 3 values, but the caller only take two should also require explicit syntax in the function declaration.
I don't find this argument compelling. There is only one return type and the return type does not influence overload resolution. To me, this sounds like an argument against use of the 'auto' type specifier at all -
... how?
My argument is that returning multiple values is conceptually identical to passing multiple values. Therefore, if the compiler gives an error when you pass 2 values to a function that takes 3, then by analogy, a function which returns 3 values should give an error if you only accept 2.
This is not about which overload you take or that you're using `auto` or anything of that sort.
what if the user meant to call a function returning some class type instead of std::tuple?
I'm gonna ignore the fact that `std::tuple` is very much a class type.
By using `auto []`, you're conceptually saying that you don't care what actual type a function returns. What you care about is that it returns a type which is decomposable into a number of values, and it is those values that you want access to.
So by saying `auto [a, b] = foo(c, d)`, you're creating a contract that checks two things:
1) `foo` is a callable-thing that can take two values as arguments.
2) `foo`, when taking 2 values, returns a type that is decomposable into 2 values.
Note that this is not "decomposable into at least 2 values". Just like `foo` is not "a callable-thing that can take at least two values as arguments".
If you want to play your default-arguments game, then we should have some decomposable machinery that explicitly has the type advertise that one or more of the values can be ignored. But this would be an innate part of the type itself.
The ability to omit declarations for unwanted bindings seems more useful to me than the opportunity to catch cases where an unintended function was called.
I'm more concerned about why you're omitting values so frequently from a multi-value return.
I don't like
auto [] = foo();
because that might lead to
auto [x,y] = Point3D(1,2,3); // drop Z
and then you lose the chance of catching an error there.
(In fact auto [] = foo(); should maybe imply foo() returns void)
Yes, I also find this similar to 'auto' vs constrained (and I prefer constrained)
auto foo = getFoo();
auto total = foo + bar;
Did you mean that to work with strings, or just Numbers? Is it OK if '/' means concatenate (for paths)? (of course I doubt getFoo() will change one day from Number to path)
Just like Concepts expose the requirements on types for templates,
Concepts can also expose the requirements on types for the code that follows the declaration.
Now, I doubt auto is as problematic as T is in unconstrained templates, but it is basically the same thing.