Please make braces a non-deduced context

287 views
Skip to first unread message

Johannes Schaub

unread,
Mar 10, 2014, 12:57:59 PM3/10/14
to std-pr...@isocpp.org
I would like to propose that we make braces a non-deduced context, so
that we can write

for(auto i = {}, size = container.size(); ...

Or that we could write

template<typename Container>
auto f(Container &c) {
if(someCondition)
return c;
return {}; // default construct
}

A set of braces have no type, and we IMHO should not try to work out
special cases, like "one element -> type of the element" or "multiple
arguments -> initializer_list" or "no = sign -> use type of the
element. = sign -> use initializer_list". In my opinion, that is just
too counter intuitive!

Instead, let's go consistent with the deduction rules who make sense
given that the braces have no types and avoid surprises!

Ville Voutilainen

unread,
Mar 10, 2014, 1:26:50 PM3/10/14
to std-pr...@isocpp.org
On 10 March 2014 18:57, Johannes Schaub <schaub....@googlemail.com> wrote:
> I would like to propose that we make braces a non-deduced context, so
> that we can write
>
> for(auto i = {}, size = container.size(); ...

What is the type of i?

>
> Or that we could write
>
> template<typename Container>
> auto f(Container &c) {
> if(someCondition)
> return c;
> return {}; // default construct
> }

Why didn't you just write
return Container{};
for the second return?

> A set of braces have no type, and we IMHO should not try to work out
> special cases, like "one element -> type of the element" or "multiple
> arguments -> initializer_list" or "no = sign -> use type of the
> element. = sign -> use initializer_list". In my opinion, that is just
> too counter intuitive!
> Instead, let's go consistent with the deduction rules who make sense
> given that the braces have no types and avoid surprises!


How much code would you like to break?

Richard Smith

unread,
Mar 10, 2014, 1:29:15 PM3/10/14
to std-pr...@isocpp.org
On Mon, Mar 10, 2014 at 9:57 AM, Johannes Schaub <schaub....@googlemail.com> wrote:
I would like to propose that we make braces a non-deduced context,

All braces, or just empty braces for copy-list-initialization?
 
so
that we can write

    for(auto i = {}, size = container.size(); ...

This would require an additional extension, to allow type deduction to fail to deduce 'auto' for some declarators in a multi-declarator declaration.

Or that we could write

    template<typename Container>
    auto f(Container &c) {
        if(someCondition)
          return c;
        return {}; // default construct
    }

A set of braces have no type, and we IMHO should not try to work out
special cases, like "one element -> type of the element" or "multiple
arguments -> initializer_list" or "no = sign -> use type of the
element. = sign -> use initializer_list". In my opinion, that is just
too counter intuitive!

Instead, let's go consistent with the deduction rules who make sense
given that the braces have no types and avoid surprises!

--

---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Johannes Schaub

unread,
Mar 10, 2014, 1:35:32 PM3/10/14
to std-pr...@isocpp.org
2014-03-10 18:26 GMT+01:00 Ville Voutilainen <ville.vo...@gmail.com>:
> On 10 March 2014 18:57, Johannes Schaub <schaub....@googlemail.com> wrote:
>> I would like to propose that we make braces a non-deduced context, so
>> that we can write
>>
>> for(auto i = {}, size = container.size(); ...
>
> What is the type of i?
>

The type is whatever is deduced by the initializer "container.size()".
"i" will have the same type. Like in this example

vector<int> v(istream_iterator<int>(cin), {});

The type of the iterator is deduced from the first argument, and the
second argument "{}" just passes a value constructed object along as
initializer of an object whose type has already been deduced.

>>
>> Or that we could write
>>
>> template<typename Container>
>> auto f(Container &c) {
>> if(someCondition)
>> return c;
>> return {}; // default construct
>> }
>
> Why didn't you just write
> return Container{};
> for the second return?
>

Because I want to save on redundantly repeating type names.

>> A set of braces have no type, and we IMHO should not try to work out
>> special cases, like "one element -> type of the element" or "multiple
>> arguments -> initializer_list" or "no = sign -> use type of the
>> element. = sign -> use initializer_list". In my opinion, that is just
>> too counter intuitive!
>> Instead, let's go consistent with the deduction rules who make sense
>> given that the braces have no types and avoid surprises!
>
>
> How much code would you like to break?
>

I don't like to break much code, but I am willing to break *some*
code. I am willing the following code

auto x = { ... };

My hope is that there is not too much code that depends on this. And
if there is a need to still accept it for implementations, compilers
can merely warn about it .. since in my proposed change it will simply
be invalid, so no harm is created by accepting it in an
implementation.

The following is required to be accepted and depends on the new semantics

auto x = {}, y = 1;

Which was ill-formed before anyway.

Ville Voutilainen

unread,
Mar 10, 2014, 1:41:44 PM3/10/14
to std-pr...@isocpp.org
On 10 March 2014 19:35, Johannes Schaub <schaub....@googlemail.com> wrote:
>>> for(auto i = {}, size = container.size(); ...
>> What is the type of i?
> The type is whatever is deduced by the initializer "container.size()".
> "i" will have the same type. Like in this example
> vector<int> v(istream_iterator<int>(cin), {});
> The type of the iterator is deduced from the first argument, and the
> second argument "{}" just passes a value constructed object along as
> initializer of an object whose type has already been deduced.
>> Why didn't you just write
>> return Container{};
>> for the second return?
> Because I want to save on redundantly repeating type names.
>> How much code would you like to break?
> I don't like to break much code, but I am willing to break *some*
> code. I am willing the following code
> auto x = { ... };
> My hope is that there is not too much code that depends on this. And
> if there is a need to still accept it for implementations, compilers
> can merely warn about it .. since in my proposed change it will simply
> be invalid, so no harm is created by accepting it in an
> implementation.
> The following is required to be accepted and depends on the new semantics
> auto x = {}, y = 1;
> Which was ill-formed before anyway.


I fail to see which part of this is intuitive, which part of it
supposedly "makes sense",
and which part of it is "no surprises". I also fail to see how this is
consistent
with deduction rules. I don't see the point of this proposed change.

hun.nem...@gmail.com

unread,
Mar 10, 2014, 1:49:07 PM3/10/14
to std-pr...@isocpp.org, schaub....@googlemail.com


On Monday, March 10, 2014 5:57:59 PM UTC+1, Johannes Schaub wrote:
I would like to propose that we make braces a non-deduced context, so
that we can write

    for(auto i = {}, size = container.size(); ...
 
for(auto i = {}, size = container.size(); ...

         
^       ^^^^
or
for(auto size = {}, size = container.size(); ...
         
^^^^       ^^^^
?

And why not
for(auto size = auto(), size = container.size(); ...
               
^^^^^^


Johannes Schaub

unread,
Mar 10, 2014, 1:49:26 PM3/10/14
to std-pr...@isocpp.org
2014-03-10 18:29 GMT+01:00 Richard Smith <ric...@metafoo.co.uk>:
> On Mon, Mar 10, 2014 at 9:57 AM, Johannes Schaub
> <schaub....@googlemail.com> wrote:
>>
>> I would like to propose that we make braces a non-deduced context,
>
>
> All braces, or just empty braces for copy-list-initialization?
>

All braces. I don't understand why we should pick some of the braces
and give special arbitrary meaning. Ask a hundred people, and I bet
you get lots of different answers like "you get a tuple", "you get an
T[]", "you get an error", "you get a non-deduced context", "you get an
initializer_list" and some other.

From the above, the "you get a non-deduced context" interpretation is
already used in function templates and I've seen people make use of it
in e.g. the shown vector construction from two iterator pairs.

Johannes Schaub

unread,
Mar 10, 2014, 1:58:12 PM3/10/14
to std-pr...@isocpp.org
I guess I owe you a usecase for the non-empty brace case, instead. But
I don't really see one currently :) Still I would prefer if we keep it
conservative and don't invent semantics that will surprise half the
people. Just my two cent.

Richard Smith

unread,
Mar 10, 2014, 5:55:44 PM3/10/14
to std-pr...@isocpp.org
WG21 is already planning on changing the meaning of auto-deduction in direct-list-initialization (see N3912, N3922). Instead of 

  auto x { 0 };

giving you a std::initializer_list, it will give you an int (and any other length of list will be ill-formed). We'll still deduce a std::initializer_list type for

  auto x = { 0 };

Making direct-list-init from empty braces a non-deduced context would thus not be a breaking change relative to the language with that DR resolved, but it doesn't actually help you, since each auto deduction must by itself deduce the type which 'auto' stands in for -- we'd still need a separate rule to allow other auto deductions to leak in for a non-deduced context.

vadim.pet...@gmail.com

unread,
Mar 12, 2014, 9:05:15 AM3/12/14
to std-pr...@isocpp.org
Can I make a dilettantish suggestion, please?

How about:

auto a1{}; // ill-formed
auto a2 = {}; // ill-formed
auto a3{10}; // int
auto a4 = {10}; // int
auto a5{10, 11}; // ill-formed
auto a6 = {10, 11}; // ill-formed

auto a1{{}}; // ill-formed
auto a2 = {{}}; // ill-formed
auto a3{{10}}; // initializer_list<int>
auto a4 = {{10}}; // initializer_list<int>
auto a5{{10, 11}}; // initializer_list<int>
auto a6 = {{10, 11}}; // initializer_list<int>

Pros:
Expressiveness: There's still a way to deduce initializer_list<T>
Future consistency: The same approach could, probably, be used to solve the evolution issue 109 (Core issue 1564, Template argument deduction from an initializer list)
Consistency: There are no surprising differences between direct-list-initialization and copy-list-initialization
Consistency 2: There are no surprising differences between initializing auto with list-initialization and other forms of initialization
Consistency 3: Currently list initialization with initializer_list can also be written in double-braced form

Cons:
Backward compatibility: More code is broken compared to N3912 (according to N3912 Chandler Carruth can measure the exact amount of broken code). I guess such code mostly lives in examples, tutorials and, may be, tests.

Rationale:
Braces {} in braced-init-lists try to denote two different things:
1) Tuple of heterogeneous arguments of "generalized constructor" (gc-braces)
2) List of homogeneous elements of initializer_list container (il-braces)

While it works when no type deduction is performed (more or less, not with templates), two notions should be separated when deducing a type.
(Step 1) Lets look at some braced-init-list we want to deduce a type from: {content}.
The outer braces are always gc-braces, and the whole braced-init-list represents a tuple of arguments.
Let "deduced type" of this tuple is the type of its content (lets call it unbraced braced-init-list).
Thus, singleton tuple of arguments is equivalent to its only argument for the purpose of type deduction (as in other forms of auto: auto b(10); auto b = 10;).
And other n-tuples (n=0, n>=2) have no deduced type.

(Step 2) Now we have unbraced braced-init-list and should deduce its type.
If unbraced braced-init-list is braced (il-braces), then it's an initializer_list and we go to Step 1 and deduce the types of its elements to deduce its type. 
Otherwize it's some expression and we just deduce its type.

So, again with rationale:

// gc-braces
auto a1{}; // ill-formed: construct (gc-braces) what?
auto a2 = {}; // ill-formed: construct (gc-braces) what?
auto a3{10}; // int: construct (gc-braces) int
auto a4 = {10}; // int: construct (gc-braces) int
auto a5{10, 11}; // ill-formed: construct (gc-braces) what?
auto a6 = {10, 11}; // ill-formed: construct (gc-braces) what?

// il-braces inside of gc-braces
auto a1{{}}; // ill-formed: construct (gc-braces) initializer_list (il-braces) of what?
auto a2 = {{}}; // ill-formed: construct (gc-braces) initializer_list (il-braces) of what?
auto a3{{10}}; // initializer_list<int>: construct (gc-braces) initializer_list (il-braces) of int
auto a4 = {{10}}; // initializer_list<int>: construct (gc-braces) initializer_list (il-braces) of int
auto a5{{10, 11}}; // initializer_list<int>: construct (gc-braces) initializer_list (il-braces) of int
auto a6 = {{10, 11}}; // initializer_list<int>: construct (gc-braces) initializer_list (il-braces) of int

// bonus: a lot of braces
auto a7 = {{{10}}}; // initializer_list<int>: construct (gc-braces) initializer_list (il-braces) of type deduced from {10}, i.e. int
auto a8 = {{{{10}}}}; // initializer_list<initializer_list<int>>: construct (gc-braces) initializer_list (il-braces) of type deduced from {{10}}, i.e. initializer_list<int>

P.S.: In any case this issue should not be discussed in separation from the evolution issue 109
P.S.2: I hope everything I wrote is not a complete nonsense

Ville Voutilainen

unread,
Mar 12, 2014, 9:10:18 AM3/12/14
to std-pr...@isocpp.org
On 12 March 2014 15:05, <vadim.pet...@gmail.com> wrote:
> Can I make a dilettantish suggestion, please?
>
> How about:
>
> auto a1{}; // ill-formed
> auto a2 = {}; // ill-formed
> auto a3{10}; // int
> auto a4 = {10}; // int
> auto a5{10, 11}; // ill-formed
> auto a6 = {10, 11}; // ill-formed

This was proposed in
http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3681.html,
but was rejected. http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3912.html
is a compromise that allows auto6 to remain valid and deduce an
initializer_list.

We can all come up with whatever interesting tweaks, but they would need to have
strong arguments for why they should be even considered.

> Cons:
> Backward compatibility: More code is broken compared to N3912 (according to
> N3912 Chandler Carruth can measure the exact amount of broken code). I guess
> such code mostly lives in examples, tutorials and, may be, tests.

He can measure it in the codebase of one company. That codebase is large and
varying, but it's still one codebase in one company.

Ville Voutilainen

unread,
Mar 12, 2014, 9:19:16 AM3/12/14
to std-pr...@isocpp.org
On 12 March 2014 15:10, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> On 12 March 2014 15:05, <vadim.pet...@gmail.com> wrote:
>> Can I make a dilettantish suggestion, please?
>>
>> How about:
>>
>> auto a1{}; // ill-formed
>> auto a2 = {}; // ill-formed
>> auto a3{10}; // int
>> auto a4 = {10}; // int
>> auto a5{10, 11}; // ill-formed
>> auto a6 = {10, 11}; // ill-formed
>
> This was proposed in
> http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3681.html,
> but was rejected. http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3912.html
> is a compromise that allows auto6 to remain valid and deduce an
> initializer_list.


And same for a4.

vadim.pet...@gmail.com

unread,
Mar 12, 2014, 9:24:42 AM3/12/14
to std-pr...@isocpp.org
This was proposed in
http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3681.html,
but was rejected. http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3912.html
is a compromise that allows auto6 to remain valid and deduce an
initializer_list.

Yes, I've read them (I personally like N3681 better).
 

We can all come up with whatever interesting tweaks, but they would need to have
strong arguments for why they should be even considered.

I mostly care about the future.
When (If) the evolution issue 109 will be resolved and/or braced lists will become expressions, it will, probably, be a terrible mess, provided N3912 is (was?) accepted.

Ville Voutilainen

unread,
Mar 12, 2014, 9:28:17 AM3/12/14
to std-pr...@isocpp.org
On 12 March 2014 15:24, <vadim.pet...@gmail.com> wrote:
>> This was proposed in
>> http://open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3681.html,
>> but was rejected.
>> http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3912.html
>> is a compromise that allows auto6 to remain valid and deduce an
>> initializer_list.
> Yes, I've read them (I personally like N3681 better).

So did I. What we like better is unfortunately irrelevant. :)

>> We can all come up with whatever interesting tweaks, but they would need
>> to have
>> strong arguments for why they should be even considered.
> I mostly care about the future.
> When (If) the evolution issue 109 will be resolved and/or braced lists will
> become expressions, it will, probably, be a terrible mess, provided N3912 is
> (was?) accepted.


The status quo is an even more terrible mess wrt auto. And I wouldn't want
to speculate whether EWG Issue 109 will go into such a direction at all.
I expect it won't. And even if it would, I think we still need what's in
N3912.

vadim.pet...@gmail.com

unread,
Mar 12, 2014, 9:40:44 AM3/12/14
to std-pr...@isocpp.org
That's just sad
Every time such a thing happens, a new c++fqa is born

Thanks :)

David Krauss

unread,
Mar 12, 2014, 9:46:55 AM3/12/14
to std-pr...@isocpp.org
The situation is not necessarily so dire. Just using my imagination, EWG 109 could be resolved along with class template argument deduction, such the types of the elements of the braced-init-list affect the type of the object initialized by it. Deducing std::initializer_list<T> could be merely a particular case of this. The lists still wouldn’t need to be expressions.

Keeping direct-list-initialization and copy-list-initialization separate, but self-consistent, just might be tenable in a sane language.

Ville Voutilainen

unread,
Mar 12, 2014, 9:51:23 AM3/12/14
to std-pr...@isocpp.org
On 12 March 2014 15:46, David Krauss <pot...@gmail.com> wrote:
> The situation is not necessarily so dire. Just using my imagination, EWG 109
> could be resolved along with class template argument deduction, such the
> types of the elements of the braced-init-list affect the type of the object
> initialized by it. Deducing std::initializer_list<T> could be merely a
> particular case of this. The lists still wouldn't need to be expressions.

EWG 109 could also be resolved as NAD, and I wouldn't lift a finger to save it.
We'll see that when EWG takes a closer look at it.

vadim.pet...@gmail.com

unread,
Mar 12, 2014, 11:54:18 AM3/12/14
to std-pr...@isocpp.org
By the way, how

auto il({1, 2, 3});

should be deduced (according to current draft, N3681 and N3912)?

7.1.6.4 auto specifier [dcl.spec.auto] 4 says:
In an initializer of the form ( expression-list ) the expression-list shall be a single assignment-expression.

And {1, 2, 3} is not a single assignment-expression (it is not an expression at all), but both g++ 4.8.2 and clang++ 3.5 (trunk 198621) -std=c++11/1y -pedantic successfully deduce std::initializer_list<int>

Richard Smith

unread,
Mar 12, 2014, 1:49:22 PM3/12/14
to std-pr...@isocpp.org
Clang trunk r203683 rejects (thanks for the bug report!).

David Krauss

unread,
Mar 12, 2014, 8:14:19 PM3/12/14
to std-pr...@isocpp.org
Was this intended? I thought the wording there was analogous to 5.2.3/1, which completely forgets to specify the case of a parenthesized braced-init-list. (It specifies the case of a single expression, and more than one “value”, but never a single element which is not an expression nor a value.)

It seems strange to make constructor calls behave differently from function calls in this context. If the same constructor has a defaulted second argument, you need the parens when specifying it but the parens are forbidden when taking the default, if this change is correct.

Richard Smith

unread,
Mar 12, 2014, 8:41:54 PM3/12/14
to std-pr...@isocpp.org
On Wed, Mar 12, 2014 at 5:14 PM, David Krauss <pot...@gmail.com> wrote:
Was this intended? I thought the wording there was analogous to 5.2.3/1, which completely forgets to specify the case of a parenthesized braced-init-list. (It specifies the case of a single expression, and more than one “value”, but never a single element which is not an expression nor a value.)

For the missing wording in 5.2.3/1, it seems pretty clear to me that the resolution to that defect will be to support the single braced-init-list case. (FWIW, it also doesn't support the no-values case where the expression was T(x...), for x an empty pack).
 
It seems strange to make constructor calls behave differently from function calls in this context. If the same constructor has a defaulted second argument, you need the parens when specifying it but the parens are forbidden when taking the default, if this change is correct.

This change is regarding the case where we're performing auto deduction, where there cannot be multiple arguments, and there is no analogue to a function call, so I'm not sure I follow your argument.

David Krauss

unread,
Mar 12, 2014, 8:59:55 PM3/12/14
to std-pr...@isocpp.org
On 2014–03–13, at 8:41 AM, Richard Smith <ric...@metafoo.co.uk> wrote:

This change is regarding the case where we're performing auto deduction, where there cannot be multiple arguments, and there is no analogue to a function call, so I'm not sure I follow your argument.

Never mind, I had forgotten the subject of conversation. First thing in the morning.

Reply all
Reply to author
Forward
0 new messages