Yet another inconsitent behavior in C++

407 views
Skip to first unread message

atch...@gmail.com

unread,
Mar 15, 2015, 9:41:39 AM3/15/15
to std-dis...@isocpp.org
Guys,
Taken from stackoverflow, but actually I came up against identical problem yesterday and was puzzled as to why my code doesn't compile:

First:

int k[] ={1,2,3,4,5};


Second:

struct slk
{
    int k[] ={1,2,3,4,5};
};

First compiles, second doesn't saying that there is too many initializers. Surely when looking at it soberly those two lines of code are identical and one being in a global namespace and second being in a namespace of struct shouldn't have any impact on compiler behavior. Why is it that in First example compiler can deduce the size and in second it either cannot or won't do it? AFAIC, First notion is more preferable from the point of view of being easier modifiable, avoiding silly mistakes when providing explicit size of array, heck, even Bjarne in either one of his books of papers suggest the first option, in the same vein as this example:
enum a { one = 1, two, three};
instead:
enum a {one = 1, two = 2, three = 3};

Why is second option not allowed?!? It goes against every logical instinct.

Nicol Bolas

unread,
Mar 15, 2015, 12:52:44 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
Because the second one is a completely different statement.

In the first case, you are defining a variable, a specific memory object with a specific location. In the second case, you are defining a type, which can be instantiated any number of times in any number of places.

Types have different rules. In particular, member initializers (what you're doing when you initialize a member like that) have different rules from variable initializers. Member initializers are shorthand for this:

struct slk
{
  slk() : k{1,2,3,4,5} {}
  int k[];
};

See the problem? Member initializers are a shorthand for writing constructor member initializers. And constructors cannot decide what the size of members are.

atch...@gmail.com

unread,
Mar 15, 2015, 1:43:22 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
Surely constructor can count those initializers and use that number as a size. That seems pretty obvious to me.

Thiago Macieira

unread,
Mar 15, 2015, 1:46:44 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 10:43:21 atch...@gmail.com wrote:
> Surely constructor can count those initializers and use that number as a
> size. That seems pretty obvious to me.

struct slk{
slk() {}
slk(int i) : k{i,7} ()
int k[] ={1,2,3,4,5};
};

Now which one does it choose?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

atch...@gmail.com

unread,
Mar 15, 2015, 1:55:09 PM3/15/15
to std-dis...@isocpp.org
It counts how many initializers is in  between { }, puts that number in array and goes from there. I'm sorry but to me this is simply illogical behavior.

atch...@gmail.com

unread,
Mar 15, 2015, 1:56:43 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
Don't want to be picky but when you say:

slk(int i) : k{i,7} ()
you really mean:
slk(int i) : k{i,7} {}
right?

Ville Voutilainen

unread,
Mar 15, 2015, 1:57:04 PM3/15/15
to std-dis...@isocpp.org
On 15 March 2015 at 19:56, <atch...@gmail.com> wrote:
> Don't want to be picky but when you say:
> slk(int i) : k{i,7} ()
> you really mean:
> slk(int i) : k{i,7} {}
> right?


http://open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3897.html

Thiago Macieira

unread,
Mar 15, 2015, 2:03:39 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 10:55:09 atch...@gmail.com wrote:
> It counts how many initializers is in between { }, puts that number in
> array and goes from there. I'm sorry but to me this is simply illogical
> behavior.

Right, so you're saying it's an array of 2 entries. Why should the other
constructor initialise it with 5?

atch...@gmail.com

unread,
Mar 15, 2015, 2:04:25 PM3/15/15
to std-dis...@isocpp.org
Mind to elaborate?
Either he is using feature not from current standard or he is using feature which has little chance of being implemented in future standard, correct?

Ville Voutilainen

unread,
Mar 15, 2015, 2:05:15 PM3/15/15
to std-dis...@isocpp.org
On 15 March 2015 at 20:04, <atch...@gmail.com> wrote:
> Mind to elaborate?
> Either he is using feature not from current standard or he is using feature
> which has little chance of being implemented in future standard, correct?


The reasons why auto-typed members are not doable apply equally well
to deducing an extent of an array member from that member's initializer.

atch...@gmail.com

unread,
Mar 15, 2015, 2:08:08 PM3/15/15
to std-dis...@isocpp.org
For the same reason why:

struct X
{
int a = 0;
X(int val):a(val)
{}
};
Default ctor will init a to zero, converting ctor will init a to whatever the val is.

atch...@gmail.com

unread,
Mar 15, 2015, 2:08:52 PM3/15/15
to std-dis...@isocpp.org
is this code correct:


struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } ()
        int k[] = { 1, 2, 3, 4, 5 };
};

atch...@gmail.com

unread,
Mar 15, 2015, 2:10:01 PM3/15/15
to std-dis...@isocpp.org
I'm not saying that this is array of size two. This is always array of size 5. Always.



On Sunday, 15 March 2015 18:03:39 UTC, Thiago Macieira wrote:

Ville Voutilainen

unread,
Mar 15, 2015, 2:11:17 PM3/15/15
to std-dis...@isocpp.org
On 15 March 2015 at 20:10, <atch...@gmail.com> wrote:
> I'm not saying that this is array of size two. This is always array of size
> 5. Always.


Go implement your proposal and come back when you've done so.

atch...@gmail.com

unread,
Mar 15, 2015, 2:19:46 PM3/15/15
to std-dis...@isocpp.org
Could you please answer to my question about code posted by thiago?
Because like I've said either he is using feature not from current standard or he is using feature which has little chance of being implemented in future standard. Which either way seems weird.

atch...@gmail.com

unread,
Mar 15, 2015, 2:26:22 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
@Thiago:
Why this compiles then:

struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {} //note that I've change those brackets into braces otherwise the code is incorrect
        int k[5] = { 1, 2, 3, 4, 5 };
};
and this doesn't:
struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}

        int k[] = { 1, 2, 3, 4, 5 };
};

According to your argument compiler wouldn't know which size to choose yet it has no problem with this code if the size is supplied which I have to stress it, should be easily done by compiler. It simply doesn't make sense and is against any logic.

Bo Persson

unread,
Mar 15, 2015, 2:58:28 PM3/15/15
to std-dis...@isocpp.org
On 2015-03-15 19:08, atch...@gmail.com wrote:
> is this code correct:
>
> struct slk{
> slk() {}
> slk(int i) : k{ i, 7 } ()
> int k[] = { 1, 2, 3, 4, 5 };
> };

No. It would make sizeof(slk) different depending on which constructor
you called. C++ doesn't support that.


>
> On Sunday, 15 March 2015 18:05:15 UTC, Ville Voutilainen wrote:
>

Thiago Macieira

unread,
Mar 15, 2015, 3:03:57 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 11:19:46 atch...@gmail.com wrote:
> Could you please answer to my question about code posted by thiago?
> Because like I've said either he is using feature not from current standard
> or he is using feature which has little chance of being implemented in
> future standard. Which either way seems weird.

Ok, let me change it to make it more explicit:


struct slk{
slk() {}
slk(int i) : k{ i, 9, 8, 7, 6 } {}
int k[] = { 1, 2 };
};

What is the size of the k array?

Daniel Krügler

unread,
Mar 15, 2015, 3:05:49 PM3/15/15
to std-dis...@isocpp.org
2015-03-15 19:58 GMT+01:00 Bo Persson <b...@gmb.dk>:
> On 2015-03-15 19:08, atch...@gmail.com wrote:
>>
>> is this code correct:
>>
>> struct slk{
>> slk() {}
>> slk(int i) : k{ i, 7 } ()
>> int k[] = { 1, 2, 3, 4, 5 };
>> };
>
>
> No. It would make sizeof(slk) different depending on which constructor you
> called. C++ doesn't support that.

I don't think that Arthur did suggest this outcome. According to his
statement in

https://groups.google.com/a/isocpp.org/d/msg/std-discussion/lzcGWLDHxr4/Hao4DRD4DpMJ

it seems that he expected that k would always have the same length of
five elements regardless of the actually chosen constructor.

- Daniel

Thiago Macieira

unread,
Mar 15, 2015, 3:14:27 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 20:05:47 Daniel Krügler wrote:
> it seems that he expected that k would always have the same length of
> five elements regardless of the actually chosen constructor.

Even if no constructor ever initialised with that declaration?

struct S {
int k[] = { 1, 2, 3 };
S() : k{4, 5} {}
};

Daniel Krügler

unread,
Mar 15, 2015, 3:16:00 PM3/15/15
to std-dis...@isocpp.org
2015-03-15 20:14 GMT+01:00 Thiago Macieira <thi...@macieira.org>:
> On Sunday 15 March 2015 20:05:47 Daniel Krügler wrote:
>> it seems that he expected that k would always have the same length of
>> five elements regardless of the actually chosen constructor.
>
> Even if no constructor ever initialised with that declaration?
>
> struct S {
> int k[] = { 1, 2, 3 };
> S() : k{4, 5} {}
> };

That is my understanding (but it seems better to have the OP respond
to this question).

- Daniel

atch...@gmail.com

unread,
Mar 15, 2015, 3:16:09 PM3/15/15
to std-dis...@isocpp.org
But now after you've changed example, you are trying to initialize array of size two with five elements which is incorrect and I would never expect that.

Guys, are you seriously not seeing anything wrong with the fact that this compiles:

struct slk{
    slk() {}

    slk(int i) : k{ i, 7 } {} //note that I've change those brackets into braces otherwise the code is incorrect
        int k[5] = { 1, 2, 3, 4, 5 };
};

and this doesn't:

struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}
        int k[] = { 1, 2, 3, 4, 5 };
};

even though the only difference between those two examples is that in one size of array is explicitly stated and in the second size of array to be expected to be deduced by compiler which need I remind you, compiler already can do it and does it in other scenarios.
Seriously guys.

atch...@gmail.com

unread,
Mar 15, 2015, 3:18:01 PM3/15/15
to std-dis...@isocpp.org
Exactly what Daniel said.

Thiago Macieira

unread,
Mar 15, 2015, 3:24:06 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 12:16:08 atch...@gmail.com wrote:
> Guys, are you seriously not seeing anything wrong with the fact that this
> compiles:

No.

atch...@gmail.com

unread,
Mar 15, 2015, 3:25:37 PM3/15/15
to std-dis...@isocpp.org
Yes, even if no ctor ever initialized, the size of k array is that size defined in class declaration. If it says:
int k[] = {0,1,2};
then that size of this array is *always* three. I seriously don't see anything strange about it. Nor unconventional. You define this array in a class, you init it. And in other constructors you can re-init it to your likings. What is wrong with that. But the point, the main point is the one where I provide identical examples and one compiles and one doesn't (which is illogical), because in one size of array is explicitly stated and in the other is not. This is wrong. This is illogical. This is hurtful.

Thiago Macieira

unread,
Mar 15, 2015, 3:29:29 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 12:25:37 atch...@gmail.com wrote:
> Yes, even if no ctor ever initialized, the size of k array is that size
> defined in class declaration. If it says:
> int k[] = {0,1,2};
> then that size of this array is *always* three. I seriously don't see
> anything strange about it. Nor unconventional. You define this array in a
> class, you init it. And in other constructors you can re-init it to your
> likings. What is wrong with that. But the point, the main point is the one
> where I provide identical examples and one compiles and one doesn't (which
> is illogical), because in one size of array is explicitly stated and in the
> other is not. This is wrong. This is illogical. This is hurtful.

I understand what you want, but that's not what the standard currently
requires.

The standard requires that you decompose

struct S {
int k[] = { 1, 2, 3 };
S() {}
};

to:

struct S {
int k[];
S() k{1, 2, 3} {}
};

For the first one to do what you've requested, we need those auto members
proposal.

atch...@gmail.com

unread,
Mar 15, 2015, 3:35:28 PM3/15/15
to std-dis...@isocpp.org
Then the behavior described by standard is illogical, wrong and hurtful. I really can't see it differently.
I would be less inclined to be irritated by this behavior if this didn't compile:

struct slk{
    slk() {}

    slk(int i) : k{ i, 7 } {} //note that I've change those brackets into braces otherwise the code is incorrect
        int k[5] = { 1, 2, 3, 4, 5 };
};

but it does. It baffles me and it irritates me.

Thiago Macieira

unread,
Mar 15, 2015, 3:41:36 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 12:35:28 atch...@gmail.com wrote:
> Then the behavior described by standard is illogical, wrong and hurtful. I
> really can't see it differently.
> I would be less inclined to be irritated by this behavior if this didn't
> compile:
>
> struct slk{
> slk() {}
> slk(int i) : k{ i, 7 } {} //note that I've change those brackets into
> braces otherwise the code is incorrect
> int k[5] = { 1, 2, 3, 4, 5 };
> };
>
> but it does. It baffles me and it irritates me.

Why should this not compile?

struct slk{
slk(int i) : k{ i, 7 } {}
int k[5] ;
};

atch...@gmail.com

unread,
Mar 15, 2015, 3:44:13 PM3/15/15
to std-dis...@isocpp.org
That's not the example I've posted. In my example array is inited with 5 literals. Compiler can easily count it. and place that number in array size.

Thiago Macieira

unread,
Mar 15, 2015, 3:54:02 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 12:44:13 atch...@gmail.com wrote:
> That's not the example I've posted. In my example array is inited with 5
> literals. Compiler can easily count it. and place that number in array size.

I know it isn't the same example. I just removed the superfluous part.

> > Why should this not compile?
> >
> > struct slk{
> > slk(int i) : k{ i, 7 } {}
> > int k[5] ;
> > };

The above should compile. Then let's add a new constructor:

struct slk{
slk() : k{1, 2, 3, 4, 5} {}
slk(int i) : k{ i, 7 } {}
int k[5] ;
};

And now let's modify the example above to move the array initialisation:

struct slk{
slk() {}
slk(int i) : k{ i, 7 } {}
int k[5] = {1, 2, 3, 4, 5};
};

Please explain why you think any of the examples above should not compile.

Dinka Ranns

unread,
Mar 15, 2015, 3:57:12 PM3/15/15
to std-dis...@isocpp.org
The intent is not to allow an array of unknown bound as a non static data member. If one wants such a feature, one should write a proposal.

However, the standard doesn't explicitly disallow what Arthur wants, and it can be read to allow for an array of unknown bound as a non static data member, provided an initializer exists. I'll ping core about this. 


--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

Nicol Bolas

unread,
Mar 15, 2015, 3:57:48 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
On Sunday, March 15, 2015 at 3:16:09 PM UTC-4, atch...@gmail.com wrote:
But now after you've changed example, you are trying to initialize array of size two with five elements which is incorrect and I would never expect that.

Guys, are you seriously not seeing anything wrong with the fact that this compiles:

struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {} //note that I've change those brackets into braces otherwise the code is incorrect
        int k[5] = { 1, 2, 3, 4, 5 };
};

and this doesn't:

struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}
        int k[] = { 1, 2, 3, 4, 5 };
};

even though the only difference between those two examples is that in one size of array is explicitly stated and in the second size of array to be expected to be deduced by compiler which need I remind you, compiler already can do it and does it in other scenarios.

Except that those "other scenarios" are very different. Namely, the object is initialized in exactly one place.

In this case, the object is being initialized in multiple place.

With your way, it is very reasonable for a user to assume that this:


struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}
        int k[] = { 1, 2, 3, 4, 5 };
};

works just as well as this:

struct slk{
    slk() {}
    slk(int i) : k{ i, 2, 3, 4, 5 } {}
        int k[] = { 2, 7 };
};

That's why it's better to just be explicit about the size. That way, you can never make this mistake.

One more thing: what you are talking about is not a defect in the standard. At best, it is an inconsistency. Therefore, what you want is a proposal. This forum is not the place to handle proposal discussions.

atch...@gmail.com

unread,
Mar 15, 2015, 3:59:04 PM3/15/15
to std-dis...@isocpp.org
They should compile. I don't see reason why they shouldn't. I also don't see reason why this shouldn't compile:


struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}
    int k[] = {1, 2, 3, 4, 5};
};

Please explain to me why do you think compiler cannot count the number of initializers and use this number as a size of  k array?
It (the compiler) does it here:

int k[] = {1, 2, 3, 4, 5};
why cannot it do it here:


struct slk{
    slk() {}
    slk(int i) : k{ i, 7 } {}
    int k[] = {1, 2, 3, 4, 5};
};

atch...@gmail.com

unread,
Mar 15, 2015, 4:01:11 PM3/15/15
to std-dis...@isocpp.org, atch...@gmail.com
No... In your example you're trying to initialize array of size two with 5 literals. That couldn't work.

Daniel Krügler

unread,
Mar 15, 2015, 4:24:34 PM3/15/15
to std-dis...@isocpp.org
2015-03-15 20:35 GMT+01:00 <atch...@gmail.com>:
> Then the behavior described by standard is illogical, wrong and hurtful. I
> really can't see it differently.
> I would be less inclined to be irritated by this behavior if this didn't
> compile:
>
> struct slk{
> slk() {}
> slk(int i) : k{ i, 7 } {} //note that I've change those brackets into
> braces otherwise the code is incorrect
> int k[5] = { 1, 2, 3, 4, 5 };
> };
>
> but it does. It baffles me and it irritates me.

I have some sympathies regarding fixing this bafflement, but such a
change of rules needs to be done carefully. Especially it shouldn't
have negative impact on the current "lazy evaluation" of
brace-or-equal-initializers. Currently the Standard says that the
brace-or-equal-initializers are not considered unless used, so
especially in type-dependent situations this is a valueable guarantee.
Consider the following example:

template<class T>
struct X
{
T array[3] = {1, 2, 3};
explicit X(T val) : array{val, val, val} {}
X() = default;
};

struct S
{
};

int main() {
X<S> x(S{});
}

This is currently well-formed, albeit S cannot be initialized by
integer values. If I understand your proposal correctly, a compiler
would *always* be required to evaluate the initializer in this
situation regardless of the chosen constructor:

template<class T>
struct X
{
T array[] = {1, 2, 3};
explicit X(T val) : array{val, val, val} {}
X() = default;
};

because it is required to analyze the initializer-expression to deduce
the member array size. I suspect that for the general case this means
that the compiler has to inspect the full-expression during
compile-time, because such initializers can be arbitrary complex and I
doubt that for a general case a compiler can just "count" the elements
without evaluating the initializer.

It may be possible to find wording that ensures that this required
analysis only happens for members that are arrays of unknown bounds,
but what I want to say here is that this wording really has to be
provided to prevent unwanted non-lazy evaluations in other situations.

- Daniel

Thiago Macieira

unread,
Mar 15, 2015, 4:46:55 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 12:59:03 atch...@gmail.com wrote:
> They should compile. I don't see reason why they shouldn't. I also don't
> see reason why this shouldn't compile:
>
> struct slk{
> slk() {}
> slk(int i) : k{ i, 7 } {}
> int k[] = {1, 2, 3, 4, 5};
> };
>
> Please explain to me why do you think compiler cannot count the number of
> initializers and use this number as a size of k array?
> It (the compiler) does it here:
>
> int k[] = {1, 2, 3, 4, 5};
> why cannot it do it here:
>
> struct slk{
> slk() {}
> slk(int i) : k{ i, 7 } {}
> int k[] = {1, 2, 3, 4, 5};
> };

We've already explained it. The above is currently required by the standard to
be equivalent to:

struct slk{
slk() : k{1, 2, 3, 4, 5} {}
slk(int i) : k{ i, 7 } {}
int k[];
};

Please note that the above triggers the "empty array trailing member"
extension, so you could do:

slk *s = new (malloc(5 * sizeof(int))) slk;

Thiago Macieira

unread,
Mar 15, 2015, 4:50:26 PM3/15/15
to std-dis...@isocpp.org
On Sunday 15 March 2015 13:01:11 atch...@gmail.com wrote:
> No... In your example you're trying to initialize array of size two with 5
> literals. That couldn't work.

Actually, in both examples the array size has undefined size.

atch...@gmail.com

unread,
Mar 16, 2015, 6:04:18 AM3/16/15
to std-dis...@isocpp.org
Yes but only because standard rules are counter-intuitive in that particular scenario.
No matter how much I will be discussing it here to me it will always be unnatural that this can be compiled:


struct slk{
     slk() {}
     slk(int i) : k{ i, 7 } {}
     int k[5] = {1, 2, 3, 4, 5};
};

and this won't:

struct slk{
     slk() {}
     slk(int i) : k{ i, 7 } {}
     int k[] = {1, 2, 3, 4, 5};
};

It's unnatural, counter-intuitive, illogical and hurtful.

atch...@gmail.com

unread,
Mar 16, 2015, 6:10:44 AM3/16/15
to std-dis...@isocpp.org
Daniel, thank you for your input. Agree with what you've said and that is basically on the lines of my thinking. Could you perhaps think of any example in which size of that array would be difficult (for compiler) to deduce from the initializer list that is.

struct slk
{
     int k[] = {1, 2, 3, 4, 5}; //this place, can you think about any example to make that size of array tricky for compiler to deduce? I can't.
};

David Krauss

unread,
Mar 16, 2015, 6:27:02 AM3/16/15
to std-dis...@isocpp.org
On 2015–03–16, at 6:04 PM, atch...@gmail.com wrote:

struct slk{
     slk() {}
     slk(int i) : k{ i, 7 } {}
     int k[] = {1, 2, 3, 4, 5};
};

It's unnatural, counter-intuitive, illogical and hurtful.

Um, how big is k supposed to be here? The size is supposed to be deduced from an unused initializer?

Deducing the size of an array from the syntax of the initializer-clause in its init-declarator, regardless of whether that really is the initializer of the object, but many will find that counter-intuitive. The line has to be drawn somewhere.

Daniel, thank you for your input. Agree with what you've said and that is basically on the lines of my thinking. Could you perhaps think of any example in which size of that array would be difficult (for compiler) to deduce from the initializer list that is.

There could be some difficulty in case of brace elision, especially with template type dependency. Unused initializers have to be compiled enough to tell initialization of multiple array elements from initialization of only the first element.

struct x {
   int a, b;
};

template< typename t >
struct s {
    t m[] = { 1, 2 };
};

s< int > a; // array size 2
s< x > b; // array size 1

atch...@gmail.com

unread,
Mar 16, 2015, 12:06:56 PM3/16/15
to std-dis...@isocpp.org
Size of k is deduced from the initializer.
Why unused initializer? Surely the array would be initialized from that initializer so that initializer is used?

In the example you've provided:
a) array size always == 2
b) t must be convertible to type of what initializer list's element type is.

Thiago Macieira

unread,
Mar 16, 2015, 12:20:01 PM3/16/15
to std-dis...@isocpp.org
On Monday 16 March 2015 03:04:18 atch...@gmail.com wrote:
> It's unnatural, counter-intuitive, illogical and hurtful.

I'm not going to argue on unnatural and counter-intuitive. Hurtful is entirely
subjective.

But the current behaviour is entirely logical. You just disagree with the
logic. That's different.

Ville Voutilainen

unread,
Mar 16, 2015, 12:27:24 PM3/16/15
to std-dis...@isocpp.org
On 16 March 2015 at 18:06, <atch...@gmail.com> wrote:
> Size of k is deduced from the initializer.
> Why unused initializer? Surely the array would be initialized from that
> initializer so that initializer is used?

We are talking about cases when the non-static data member initializer
is not used -
cases where a ctor-initializer overrules it. Such as

struct X
{
vector<int> v = {1,2,3,4};
X() {}
X(initializer_list<int> i) : v(i) /* non-static data member
initializer for v is not used */ {};
};

If you do X x; x.v has four elements. If you do X x{1,2}; x.v has two
elements. The same logic
applies to an initializer for an array. You're suggesting that there
would be an exception to
these rules where an unsized array would always deduce its size from a
non-static data
member initializer, even when that initializer is not used to actually
initialize the array.

David Rodríguez Ibeas

unread,
Mar 16, 2015, 12:29:46 PM3/16/15
to std-dis...@isocpp.org
On Mon, Mar 16, 2015 at 12:06 PM, <atch...@gmail.com> wrote:
Size of k is deduced from the initializer.
Why unused initializer? Surely the array would be initialized from that initializer so that initializer is used?

Whether that initializer is used or not depends on whether the initializer list in the constructor explicitly mentions the member or it doesn't. In the code you presented:


 slk(int i) : k{ i, 7 } {}

The 'k' is explicitly mentioned and thus the initializer in the class definition is *unused* (if you remove the member initialization from the constructor, then it would be used).  Honestly, the size of the array in the case of a class is important enough that I believe it should be explicitly stated --i.e. I am fine with the current rules.  To objects of the same type need to have the same size, having the compiler deduce the size based on the initialization is fine for a local variable, but this is not for one object, but a class of objects.
 
In the example you've provided:
a) array size always == 2
b) t must be convertible to type of what initializer list's element type is.

Why would it? If you wrote that in the stack you would get sizes 2 and 1 for the two cases.  I find the inconsistency on the variable vs. member declaration less problematic than the alternative.  Currently you get a consistent error the syntax is disallowed for class definitions, otherwise you would get either different behavior or an error that happens some times but not always.

    David

Richard Smith

unread,
Mar 16, 2015, 2:17:23 PM3/16/15
to std-dis...@isocpp.org
On Mon, Mar 16, 2015 at 3:10 AM, <atch...@gmail.com> wrote:
Daniel, thank you for your input. Agree with what you've said and that is basically on the lines of my thinking. Could you perhaps think of any example in which size of that array would be difficult (for compiler) to deduce from the initializer list that is.

template<int> struct T;
template<> struct T<1> { template<int,int> char a(int); };
template<> struct T<2> { static int a; };
struct X {
  char a[] = { T<sizeof(X)>::a<0, 0>(0) };
};

What's the bound of X::a, 1 or 2? (Note that X is a complete type in this context, because the initializer is handled as if it appeared in a constructor, so sizeof(X) is valid.)

--

Ville Voutilainen

unread,
Mar 16, 2015, 2:28:16 PM3/16/15
to std-dis...@isocpp.org
On 16 March 2015 at 20:17, Richard Smith <ric...@metafoo.co.uk> wrote:
> On Mon, Mar 16, 2015 at 3:10 AM, <atch...@gmail.com> wrote:
>>
>> Daniel, thank you for your input. Agree with what you've said and that is
>> basically on the lines of my thinking. Could you perhaps think of any
>> example in which size of that array would be difficult (for compiler) to
>> deduce from the initializer list that is.
>
>
> template<int> struct T;
> template<> struct T<1> { template<int,int> char a(int); };
> template<> struct T<2> { static int a; };
> struct X {
> char a[] = { T<sizeof(X)>::a<0, 0>(0) };
> };
>
> What's the bound of X::a, 1 or 2? (Note that X is a complete type in this
> context, because the initializer is handled as if it appeared in a
> constructor, so sizeof(X) is valid.)


Except X can't be complete before it knows its size, which it can't know before
it uses the initializer for a, which it can't do because that requires
knowing the
size, and thus the merry-go-round spins until the compiler possibly
says "oh, snap, stop". :)

Chris Hallock

unread,
Mar 16, 2015, 3:20:17 PM3/16/15
to std-dis...@isocpp.org
On Sunday, March 15, 2015 at 3:57:12 PM UTC-4, Dinka wrote:
However, the standard doesn't explicitly disallow what Arthur wants, and it can be read to allow for an array of unknown bound as a non static data member, provided an initializer exists.

 Actually, as Richard pointed out to me in an earlier discussion, the standard does clearly prohibit this, in 8.3.4/3: "An array bound may also be omitted when the declarator is followed by an initializer (8.5)." NSDMIs are not initializers. They are brace-or-equal-initializers instead. An initializer can consist of a brace-or-equal-initializer but not the other way around.

Dinka Ranns

unread,
Mar 16, 2015, 4:08:18 PM3/16/15
to std-dis...@isocpp.org

yes, Richard has since pointed out the same thing to me... :)

Thiago Macieira

unread,
Mar 16, 2015, 5:29:14 PM3/16/15
to std-dis...@isocpp.org
On Monday 16 March 2015 20:28:15 Ville Voutilainen wrote:
> > template<int> struct T;
> > template<> struct T<1> { template<int,int> char a(int); };
> > template<> struct T<2> { static int a; };
> > struct X {
> >
> > char a[] = { T<sizeof(X)>::a<0, 0>(0) };
> >
> > };
> >
> > What's the bound of X::a, 1 or 2? (Note that X is a complete type in this
> > context, because the initializer is handled as if it appeared in a
> > constructor, so sizeof(X) is valid.)
>
> Except X can't be complete before it knows its size, which it can't know
> before it uses the initializer for a, which it can't do because that
> requires knowing the
> size, and thus the merry-go-round spins until the compiler possibly
> says "oh, snap, stop".

template<int> struct T;
template<> struct T<1> { template<int,int> char a(int); };
template<> struct T<2> { static int a; };
struct X {
X();
char a[];;
};

X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
{}

Are you saying that X isn't complete in the X::X constructor declaration?

Ville Voutilainen

unread,
Mar 16, 2015, 5:54:56 PM3/16/15
to std-dis...@isocpp.org
On 16 March 2015 at 23:29, Thiago Macieira <thi...@macieira.org> wrote:
> On Monday 16 March 2015 20:28:15 Ville Voutilainen wrote:
>> > template<int> struct T;
>> > template<> struct T<1> { template<int,int> char a(int); };
>> > template<> struct T<2> { static int a; };
>> > struct X {
>> >
>> > char a[] = { T<sizeof(X)>::a<0, 0>(0) };
>> >
>> > };
>> >
>> > What's the bound of X::a, 1 or 2? (Note that X is a complete type in this
>> > context, because the initializer is handled as if it appeared in a
>> > constructor, so sizeof(X) is valid.)
>>
>> Except X can't be complete before it knows its size, which it can't know
>> before it uses the initializer for a, which it can't do because that
>> requires knowing the
>> size, and thus the merry-go-round spins until the compiler possibly
>> says "oh, snap, stop".
>
> template<int> struct T;
> template<> struct T<1> { template<int,int> char a(int); };
> template<> struct T<2> { static int a; };
> struct X {
> X();
> char a[];;
> };
>
> X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
> {}
>
> Are you saying that X isn't complete in the X::X constructor declaration?


No. The question is whether the class can be complete before its size
is known. Last I checked, the answer to that question is no.

Thiago Macieira

unread,
Mar 16, 2015, 6:03:57 PM3/16/15
to std-dis...@isocpp.org
On Monday 16 March 2015 23:54:53 Ville Voutilainen wrote:
> > template<int> struct T;
> > template<> struct T<1> { template<int,int> char a(int); };
> > template<> struct T<2> { static int a; };
> > struct X {
> >
> > X();
> > char a[];;
> >
> > };
> >
> > X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
> > {}
> >
> > Are you saying that X isn't complete in the X::X constructor declaration?
>
> No. The question is whether the class can be complete before its size
> is known. Last I checked, the answer to that question is no.

Agreed, but I'm missing how that is relevant to the discussion.

Unless you're talking about the case in which the compiler must evaluate the
nsdmi initialiser inside the class.

Richard Smith

unread,
Mar 16, 2015, 6:05:50 PM3/16/15
to std-dis...@isocpp.org
On Mon, Mar 16, 2015 at 2:29 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Monday 16 March 2015 20:28:15 Ville Voutilainen wrote:
> > template<int> struct T;
> > template<> struct T<1> { template<int,int> char a(int); };
> > template<> struct T<2> { static int a; };
> > struct X {
> >
> >   char a[] = { T<sizeof(X)>::a<0, 0>(0) };
> >
> > };
> >
> > What's the bound of X::a, 1 or 2? (Note that X is a complete type in this
> > context, because the initializer is handled as if it appeared in a
> > constructor, so sizeof(X) is valid.)
>
> Except X can't be complete before it knows its size, which it can't know
> before it uses the initializer for a, which it can't do because that
> requires knowing the
> size, and thus the merry-go-round spins until the compiler possibly
> says "oh, snap, stop".

template<int> struct T;
template<> struct T<1> { template<int,int> char a(int); };
template<> struct T<2> { static int a; };
struct X {
  X();
  char a[];;
};

X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
{}

Are you saying that X isn't complete in the X::X constructor declaration?

That's ill-formed because X::a has an incomplete type. X would be complete in the X::X constructor, but the program was ill-formed before we got there. (And the example I gave is ill-formed for essentially the same reason.)

Thiago Macieira

unread,
Mar 16, 2015, 7:11:28 PM3/16/15
to std-dis...@isocpp.org
On Monday 16 March 2015 15:05:49 Richard Smith wrote:
> > template<int> struct T;
> > template<> struct T<1> { template<int,int> char a(int); };
> > template<> struct T<2> { static int a; };
> > struct X {
> >
> > X();
> > char a[];;
> >
> > };
> >
> > X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
> > {}
> >
> > Are you saying that X isn't complete in the X::X constructor declaration?
>
> That's ill-formed because X::a has an incomplete type. X would be complete
> in the X::X constructor, but the program was ill-formed before we got
> there. (And the example I gave is ill-formed for essentially the same
> reason.)

Ah, I see what you mean.

I tested it with Clang and it accepted. It triggered the extension for flexible
trailing types and assumes that the caller allocated enough space for the
elements used in the array.

Richard Smith

unread,
Mar 16, 2015, 7:37:47 PM3/16/15
to std-dis...@isocpp.org
On Mon, Mar 16, 2015 at 4:11 PM, Thiago Macieira <thi...@macieira.org> wrote:
On Monday 16 March 2015 15:05:49 Richard Smith wrote:
> > template<int> struct T;
> > template<> struct T<1> { template<int,int> char a(int); };
> > template<> struct T<2> { static int a; };
> > struct X {
> >
> >   X();
> >   char a[];;
> >
> > };
> >
> > X::X() : a{ T<sizeof(X)>::a<0, 0>(0) }
> > {}
> >
> > Are you saying that X isn't complete in the X::X constructor declaration?
>
> That's ill-formed because X::a has an incomplete type. X would be complete
> in the X::X constructor, but the program was ill-formed before we got
> there. (And the example I gave is ill-formed for essentially the same
> reason.)

Ah, I see what you mean.

I tested it with Clang and it accepted. It triggered the extension for flexible
trailing types and assumes that the caller allocated enough space for the
elements used in the array.

Yep, Clang accepts C99 flexible array members in C++ as a GNU extension; use -std=c++11 -pedantic-errors to compile in strictly-conforming mode. (That mode also rejects your double-semicolon...)

Faisal Vali

unread,
Mar 16, 2015, 8:12:53 PM3/16/15
to std-dis...@isocpp.org
On Mon, Mar 16, 2015 at 1:17 PM, Richard Smith <ric...@metafoo.co.uk> wrote:
> On Mon, Mar 16, 2015 at 3:10 AM, <atch...@gmail.com> wrote:
>>
>> Daniel, thank you for your input. Agree with what you've said and that is
>> basically on the lines of my thinking. Could you perhaps think of any
>> example in which size of that array would be difficult (for compiler) to
>> deduce from the initializer list that is.
>
>
> template<int> struct T;
> template<> struct T<1> { template<int,int> char a(int); };
> template<> struct T<2> { static int a; };
> struct X {
> char a[] = { T<sizeof(X)>::a<0, 0>(0) };
> };
>
> What's the bound of X::a, 1 or 2? (Note that X is a complete type in this
> context, because the initializer is handled as if it appeared in a
> constructor, so sizeof(X) is valid.)
>
>> struct slk
>> {
>> int k[] = {1, 2, 3, 4, 5}; //this place, can you think about any
>> example to make that size of array tricky for compiler to deduce? I can't.
>> };
>>

If someone really had compelling use cases for declaring non-static
data members with the initial array bound omitted, one could craft a
"scheme" to make it work.

For example:
If we agree that in order to determine the size of the class prior to
parsing the default initializers, the only information we need from
the initializer list is the number of elements in it, and we agree
that whatever we determine to be the number during the initial lexing
(tokenizing) for each of those unknown bounds should be the same
number when we parse it at the end of the class definition (i.e. the
size of the class has been sealed), then:
- the number of elements in the initializer-list during initial
lexing can be determined by the number of commas un-enclosed within
parens or additional braces within the outer-most initializer-list
- at the closing brace of the class definition, that number is used
to compute the size of the class
- when parsing the default initializers of the class, if the size of
the omitted array bound is different from the initial count, a helpful
error could be emitted

So if the compiler errors out, you could re-write the default
initializer as : { (T<sizeof(X)>::a < 0 ), 0 > (0) }.

I'm certainly not saying this is a road you should try and take us
down - it seems not entirely without peril, and the reward seems
unclear ...

ajay kumar

unread,
Oct 4, 2015, 7:16:30 AM10/4/15
to ISO C++ Standard - Discussion, atch...@gmail.com

http://comsciguide.blogspot.com/2015/01/enum-make-u-r-names-as-data-types.html
On Sunday, March 15, 2015 at 7:11:39 PM UTC+5:30, atch...@gmail.com wrote:
Guys,
Taken from stackoverflow, but actually I came up against identical problem yesterday and was puzzled as to why my code doesn't compile:

First:

int k[] ={1,2,3,4,5};


Second:

struct slk
{
    int k[] ={1,2,3,4,5};
};

First compiles, second doesn't saying that there is too many initializers. Surely when looking at it soberly those two lines of code are identical and one being in a global namespace and second being in a namespace of struct shouldn't have any impact on compiler behavior. Why is it that in First example compiler can deduce the size and in second it either cannot or won't do it? AFAIC, First notion is more preferable from the point of view of being easier modifiable, avoiding silly mistakes when providing explicit size of array, heck, even Bjarne in either one of his books of papers suggest the first option, in the same vein as this example:
enum a { one = 1, two, three};
instead:
enum a {one = 1, two = 2, three = 3};

Why is second option not allowed?!? It goes against every logical instinct.

Johannes Schaub

unread,
Oct 4, 2015, 8:10:17 AM10/4/15
to std-dis...@isocpp.org
Not sure whether you are aware of the workaround, but you can get the
correct type using `decltype`

template<typename T>
using id = T;

#define inclass_array(T, N, ...) decltype(id< T > __VA_ARGS__) N =
__VA_ARGS__

struct slk
{
inclass_array(int[], k, {1,2,3,4,5});
};


2015-03-15 14:41 GMT+01:00 <atch...@gmail.com>:
> Guys,
> Taken from stackoverflow, but actually I came up against identical problem
> yesterday and was puzzled as to why my code doesn't compile:
>
> First:
>
> int k[] ={1,2,3,4,5};
>
>
> Second:
>
> struct slk
> {
> int k[] ={1,2,3,4,5};
> };
>
>
> First compiles, second doesn't saying that there is too many initializers.
> Surely when looking at it soberly those two lines of code are identical and
> one being in a global namespace and second being in a namespace of struct
> shouldn't have any impact on compiler behavior. Why is it that in First
> example compiler can deduce the size and in second it either cannot or won't
> do it? AFAIC, First notion is more preferable from the point of view of
> being easier modifiable, avoiding silly mistakes when providing explicit
> size of array, heck, even Bjarne in either one of his books of papers
> suggest the first option, in the same vein as this example:
> enum a { one = 1, two, three};
> instead:
> enum a {one = 1, two = 2, three = 3};
>
> Why is second option not allowed?!? It goes against every logical instinct.
>

Johannes Schaub

unread,
Oct 4, 2015, 8:15:57 AM10/4/15
to std-dis...@isocpp.org
GCC 5.2 categorizes the type as a reference (huh?.. clang doesn't do
that...), you can easily get around this by applying
remove_reference..

typename std::remove_reference_t<decltype(id< T > __VA_ARGS__)>

Johannes Schaub

unread,
Oct 4, 2015, 8:40:51 AM10/4/15
to std-dis...@isocpp.org
The same is true about qualified-id vs "nested-name-specifier
unqualified-id". But still, statements about "qualified-id" apply to
situations were we see only a "nested-name-specifier unqualified-id"
or "nested-name-specifier identifier".

For example: "If the qualified-id in a typename-specifier does not
denote a type, the program is ill-formed." at 14.6p3.

So I wouldn't say the Standard "clearly prohibits" that based on
syntactical nuances that the Standard has no clear concept of.

Chris Hallock

unread,
Oct 4, 2015, 2:38:16 PM10/4/15
to ISO C++ Standard - Discussion


That would be a great counter-example, except that 5.1.1/9 says:

"A nested-name-specifier that denotes a class, optionally followed by the keyword template (14.2), and then followed by the name of a member of either that class (9.2) or one of its base classes (Clause 10), is a qualified-id [...]"

Which is one of those unfortunate cases where the grammar notations don't fully describe the grammar.

Still, I concede that claiming that the Standard "clearly" prohibits deduced array bounds for member arrays is overstating the clarity of the Standard. Ideally, that scenario would be mentioned in a note or example.

Johannes Schaub

unread,
Oct 4, 2015, 3:33:05 PM10/4/15
to std-dis...@isocpp.org

This disambiguity is resooved by referring to the nonterminal in italic and to the defined term in nonitalic. Yet the example I mentioned does refer to the grammar nonterminal since the use is in italic.

Reply all
Reply to author
Forward
0 new messages