Point p;
p = {-1, -1};
if(p == {-1, -1}) return;
if({-1, -1} == p) return;
if(-1 == p.x)
if({-1, -1} == p)
const auto e = car ? car->engine() : {};
Hello, in a recent search about why is Initializer List limited so much (cant be used in arithmetics, assignment, etc), I came to this post : http://stackoverflow.com/a/11445905/362515Basically the problem is, Initializer List is impossible to work in any situation where operators are used - parsing such an expression will be to complex.That is fine.However support in limited, but well defined scenarios is possible and I believe the benefits will still outweigh the confusion why it is not working in some contexts.What is more there is already a confusion why it is not working in "similar contexts"!For example from the user perspective assignment and comparison are "similar" contexts of use - one is "are you that thing" the other is "become that thing".Needless to say the syntax is, as we know, dangerously close "==" vs "=".If for instance is allowed only the type of the RHS in operator to be deduced from an init-list and expression is dissuaded to start with a init-list, not only many if not all use cases will be possible, but it will be somewhat easier to teach why the other case is to available.Back to the example - it is hard to explain why this is working:
Point p;
p = {-1, -1};but this is not:
if(p == {-1, -1}) return;
But this is actually relatively easy to explain why it's not working:
if({-1, -1} == p) return;
Bonus:This must work, it really must work:We know it does not but it should, no reason why not - first arg is given, second arg is given, the the third must the same type as the second, so we have everything to deduce it!
const auto e = car ? car->engine() : {};
if(Point{-1, -1} == p) return;
there is no way to know in advance (for a novice) which operation will accepts {} and which not - the user will have to learn, through trail and error, which combo is hardcoded to work.
I am willing to "sacrifice" symmetry to get 90%+ of the usage.
The original paper is also aware of it. It is only a technical limitation.
We suspect that allowing initializer lists as right-hand, but nor [sic] as left-hand arguments to most operators is too much of a kludge
Am 18.09.2016 15:08 schrieb "Nicol Bolas" <jmck...@gmail.com>:
>
> On Sunday, September 18, 2016 at 8:41:39 AM UTC-4, mihailn...@gmail.com wrote:
>>
>> Hello, in a recent search about why is Initializer List limited so much (cant be used in arithmetics, assignment, etc), I came to this post : http://stackoverflow.com/a/11445905/362515
>>
>> Basically the problem is, Initializer List is impossible to work in any situation where operators are used - parsing such an expression will be to complex.
>>
>> That is fine.
>>
>> However support in limited, but well defined scenarios is possible and I believe the benefits will still outweigh the confusion why it is not working in some contexts.
>> What is more there is already a confusion why it is not working in "similar contexts"!
>>
>> For example from the user perspective assignment and comparison are "similar" contexts of use - one is "are you that thing" the other is "become that thing".
>> Needless to say the syntax is, as we know, dangerously close "==" vs "=".
>>
>> If for instance is allowed only the type of the RHS in operator to be deduced from an init-list and expression is dissuaded to start with a init-list, not only many if not all use cases will be possible, but it will be somewhat easier to teach why the other case is to available.
>>
>> Back to the example - it is hard to explain why this is working:
>> Point p;
>> p = {-1, -1};
>>
>> but this is not:
>>
>> if(p == {-1, -1}) return;
>>
>
> Because one is assignment while the other is equality testing. They're two different things.
>
You habe just explained it in terms of how the compiler deduces the illformedness. "one is this and one is that. And the language forbids that and now this". That is not an explanation of the "why" but more of the "how" i guess,
> Why is that hard to explain?
>
>>
>> But this is actually relatively easy to explain why it's not working:
>> if({-1, -1} == p) return;
>>
>
> Um, no. If we allow `X == Y` to work, then it makes sense that we allow `Y == X` to work. To disallow one and not the other is far more bizarre than allowing one operation but forbidding a completely different one.
>
I agree.
>> Bonus:
>> This must work, it really must work:
>> const auto e = car ? car->engine() : {};
>> We know it does not but it should, no reason why not - first arg is given, second arg is given, the the third must the same type as the second, so we have everything to deduce it!
>
>
> Again, that goes back to consistency. `!car ? {} : car->engine()` is conceptually the same, yet it wouldn't work under your scheme.
>
Why? Two arg types are known. The third arg types will just initialize a value of that one branches type.
This is conceptually remotely consistent with the treatment of init list as nondeduced contexts, in which the initlist will initialize a parameter type that is determined elsewhere.
Am 18.09.2016 15:08 schrieb "Nicol Bolas" <jmck...@gmail.com>:
>
> On Sunday, September 18, 2016 at 8:41:39 AM UTC-4, mihailn...@gmail.com wrote:
>>
>> Hello, in a recent search about why is Initializer List limited so much (cant be used in arithmetics, assignment, etc), I came to this post : http://stackoverflow.com/a/11445905/362515
>>
>> Basically the problem is, Initializer List is impossible to work in any situation where operators are used - parsing such an expression will be to complex.
>>
>> That is fine.
>>
>> However support in limited, but well defined scenarios is possible and I believe the benefits will still outweigh the confusion why it is not working in some contexts.
>> What is more there is already a confusion why it is not working in "similar contexts"!
>>
>> For example from the user perspective assignment and comparison are "similar" contexts of use - one is "are you that thing" the other is "become that thing".
>> Needless to say the syntax is, as we know, dangerously close "==" vs "=".
>>
>> If for instance is allowed only the type of the RHS in operator to be deduced from an init-list and expression is dissuaded to start with a init-list, not only many if not all use cases will be possible, but it will be somewhat easier to teach why the other case is to available.
>>
>> Back to the example - it is hard to explain why this is working:
>> Point p;
>> p = {-1, -1};
>>
>> but this is not:
>>
>> if(p == {-1, -1}) return;
>>
>
> Because one is assignment while the other is equality testing. They're two different things.
>
> Why is that hard to explain?
>
>>
>> But this is actually relatively easy to explain why it's not working:
>> if({-1, -1} == p) return;
>>
>
> Um, no. If we allow `X == Y` to work, then it makes sense that we allow `Y == X` to work. To disallow one and not the other is far more bizarre than allowing one operation but forbidding a completely different one.
>
However, C++ already has precedent of this asymmetry. Member operator overloads are already looked up only in the left hand side class object.
Am 18.09.2016 14:48 schrieb "D. B." <db0...@gmail.com>:
>
> Um, no. It works in functions etc because they know what type to expect. Arbitrary expression do not. So they should not attempt to guess what the user is meaning. Such guesses will be wrong far more than they would be right.
>
>
I don't see a need for guessing. The same procedure that apples to op= should apply to op@. The brace is treated as a function argument to overloaded or builtin operators. For example "3 + { 1.2 }" would be illformed because it contains a narrowing conversion (UAC will apply only after list initialization was done, to have both the cake and eat it).
> Um, no. If we allow `X == Y` to work, then it makes sense that we allow `Y == X` to work. To disallow one and not the other is far more bizarre than allowing one operation but forbidding a completely different one.
>However, C++ already has precedent of this asymmetry. Member operator overloads are already looked up only in the left hand side class object.
//does not:
if(-1 == x)
//works:
if(x == -1)
if(p == {-1, -1})
//vs
if({-1, -1} == p)
> Um, no. If we allow `X == Y` to work, then it makes sense that we allow `Y == X` to work. To disallow one and not the other is far more bizarre than allowing one operation but forbidding a completely different one.
>However, C++ already has precedent of this asymmetry. Member operator overloads are already looked up only in the left hand side class object.
I want to stress out again, it is not true asymmetry. It is not like "a value of type Y cannot be compared (or looked up to select operator) to a value of type X, although the opposite is true".
Wait a minute.
You want to have this feature, in your words, because it was "hard to explain why this is working: but this is not:". Yet your explanation of why one kind of asymmetry is not "true asymmetry" is based on the complex question of what is a syntactic failure and what is not. That is, what a "valid statement" is.
It seems to me that you're trading one form of hard to explain incongruity with another. And that's assuming we buy your belief that the current state of things is in fact hard to explain.
...
Here's what a compiler expert told me, after I asked for his input:
---snip---
> Why doesn't something like
>
> pair<int, int> p;
>
> if (p == {1,2})
>
> work? Is there some parsing difficulty involved? I guess making
>
> if ({1,2} == p)
>
> work might lead to parsing nightmares?
Yes, it would be a bit painful to specify, I think. We’d presumably
want an initializer-clause to become a valid primary-expression.
Expression can appear alone in statements (expression statements), but
statements can be blocks with embedded expression statements. So:
{ 1+2+3+4; }
would look like an expression until you hit the “;”. I suppose we
could evade that issue by only considering “{“ to start an expression
if no statement can start at that place.
I remember Bjarne discussing this in the early list-init papers, but I
don’t recall the details of the conclusion (I do seem to recall that
he right away decided to only allow them on the right hand side of
assignments, when it came to non-initializer contexts).
My gut feeling is that there isn’t an insurmountable problem with the
idea though (assuming we allow a leading “{“ in a statement to always
resolve to a block statement).
---snap---
PointList list;
//expected and much needed behavior:
list << {-1, -1};
// useless anyway:
{-1, -1} << list;
{-1, -1} >> list;
I am *quite* happy to argue that `{...} == expr` should not be allowed.
More accurately, it should be allowed iff there exists a conversion from
`expr` to std::initializer_list *and* there exists an operator== for
std::initializer_list, just like if the LHS was any other type. (IOW,
since those conditions are false, it follows that the operation is not
permitted, *according to the same rules that apply to any other LHS
type*. This is therefore logical and consistent with the language as a
whole.)
The perfect should not be the enemy of the good.