5

In example 14 under [dcl.init.list], the standard uses the term "top-level" when describing the semantics of code using list-initialization, in the context of narrowing conversions. I don't know what this means.

This code executes without errors:

int f(int a) { return 1;}

int main() {
    int a[] = {f(2), f(2.0)};  
    int b[] = {f(2.0), f(2)}; // No error because: double-to-int conversion is not at the top-level
}

I also tried the following. I figured it is nothing to do with the order of initialization:

int f(int a) { return 1;}

int main() {
    int a[] = {f(2), f(2.0)};  
    int b[] = {f(2.0), f(2)}; // double-to-int conversion is not at the top-level
    //int c[] = {f(2147483645.0f), f(2)}; // This is erroring out due to narrowing.
    int d[] = {f(2), f(2.0)};  // Now I'm sure top-level doesn't mean the order of initialization. 
}

I want to know what is a top-level? The documentations here and here do not describe it.

The reason I'm curious about this term is because I'm trying to understand when would list-initializers work when narrowing conversion is implicitly invoked.

I'm also not sure about the terminology. For example, is there something like a top-level class, or a top-level type, or a top-level list-initializer?

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
Jerry Ajay
  • 1,084
  • 11
  • 26
  • https://godbolt.org/z/oj4jv1 so basically you are "braking under wrong tree". Problem has nothing to initialization list. – Marek R Nov 11 '20 at 18:27
  • This is not about lists or top-level, but about what the compiler (gcc) chooses to warn about. See [Why doesn't g++ -Wconversion warn about conversion of double to long int when double is constant?](https://stackoverflow.com/questions/24677505/why-doesnt-g-wconversion-warn-about-conversion-of-double-to-long-int-when-do). – dxiv Nov 11 '20 at 18:30
  • not sure how to say it without sounding "super clever" (or "super dumb"), but the standard isnt written as a documentation of C++ for users. I also don't understand what that sentence in the standard means, but maybe thats not that important. – 463035818_is_not_an_ai Nov 11 '20 at 18:31
  • 1
    _"No error because: double-to-int conversion is not at the top-level"_ Where did the phrase come from then? Was it a warning? Something else? So far, from the information we have, the only person who's used it is you ;) – Asteroids With Wings Nov 11 '20 at 18:34
  • It's in the standard documentation: http://eel.is/c++draft/dcl.init.list#example-14 – Jerry Ajay Nov 11 '20 at 18:37
  • 1
    Please add that crucial detail to your question!! (The standard is not "documentation", however) – Asteroids With Wings Nov 11 '20 at 18:37
  • 2
    @AsteroidsWithWings one of the "here" links points to the standard where it says "Note 7: As indicated above, such conversions are not allowed at the top level in list-initializations." Could be more clearly indicated in the question, yes – 463035818_is_not_an_ai Nov 11 '20 at 18:37
  • @idclev463035818 Yes, "here" is a wonderful link text – Asteroids With Wings Nov 11 '20 at 18:39
  • and "The documentations here" is a mislabeling. I commented on that before, but didn't get the actual point across: The standard is no "documentation" – 463035818_is_not_an_ai Nov 11 '20 at 18:40
  • 1
    @idclev463035818 I haz teh fixed its – Asteroids With Wings Nov 11 '20 at 18:44
  • Awesome, thanks. You beat me by 1 sec ;-) – Jerry Ajay Nov 11 '20 at 18:45
  • 1
    sometimes words are just words and have not more meaning than what they get by the context. Yes, there can be a "top-level class" just like if you stack 3 boxes on top of each other there is a top-level box, and everybody will understand which box that refers to – 463035818_is_not_an_ai Nov 11 '20 at 18:46

4 Answers4

7

This is not a strictly defined term. But in [dcl.init.list]/note-7 that you linked, "at the top level" seems to mean "written directly in a braced list, rather than in a nested expression".

So, in int x[] = {1.0, f(2.0)};, 1.0 is at the top level, because it's written directly in the braced list, but 2.0 is not because it's nested in a function-call expression.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Accepting this as the solution since it clearly points out `int e[] = {2.0};` to be a narrowing-error in the context of list-initialization. @Asteroids With Wings , your comments helped formulate the thinking. However, I'm able to accept only one. – Jerry Ajay Nov 11 '20 at 18:55
  • @Jerry The very examples in the standard that you cited yourself already pointed that out, but sure ;) – Asteroids With Wings Nov 11 '20 at 21:03
3

This is not a rigorously defined C++ standard term. Instead, it was just meant in the sloppy English language sense. You can imagine someone using other phrases, like "earlier in the function" or "the corresponding header file", which are not technically C++ terms but would be widely understood.

In this case, they're referring to a higer level of nesting. For example, consider this snippet:

void foo() {
    int j[2][2] = {{3, 4}, {5, 6}};
}

Clearly 4 is more deeply nested then {3, 4}, which in turn is more nested than {{3, 4}, {5, 6}}. In fact, {{3, 4}, {5, 6}} is not nested at all. The author of the text you read would call this at the "top level".

Another author might argue that foo is on the "top level", so it depends on context to an extent. In the quotation you posted, the meaning is clear though.

Arthur Tacca
  • 8,833
  • 2
  • 31
  • 49
  • and the standard does briefly use this term non-normatively, when discussing certain conversions in this context ([note 7](http://eel.is/c++draft/dcl.init.list#note-7)) though I can't actually see where `double-to-int` is constrained o.O – Asteroids With Wings Nov 11 '20 at 18:34
  • 1
    *"In the quotation you posted, the meaning is clear though."* If the "level" means "level of nesting braces", can you give an example where a narrowing conversion is allowed due to it not being at the top level? – HolyBlackCat Nov 11 '20 at 18:34
  • @HolyBlackCat Good timing - see note 7, which isn't the rule you're looking for, but does sum it up – Asteroids With Wings Nov 11 '20 at 18:35
  • I could write something like this and it works fine: `int e = f(2.0);` . AFAIK, is it not top-level (and suppose to error-out?) – Jerry Ajay Nov 11 '20 at 18:35
  • @Jerry You could write just `int n = 2.0;`. The function call, the arrays and the nesting are irrelevant. – dxiv Nov 11 '20 at 18:37
  • @HolyBlackCat In fact never mind that, the example you're looking for is in the question. It's the crux of the question. – Asteroids With Wings Nov 11 '20 at 18:38
  • @Jerry In this context, we're talking about top-level in the list-initialization. Your new example has no list-initialization so it's completely irrelevant. – Asteroids With Wings Nov 11 '20 at 18:39
  • Ok, how about this: `int e[] = {f(2.0)};` ? Now its a list-initialization at a so-called top-level. – Jerry Ajay Nov 11 '20 at 18:40
  • No it's not, it's okay for the same reason as the example in your question (which is almost identical) - `f(2.0)` is at the top level but `2.0` is not – Asteroids With Wings Nov 11 '20 at 18:41
  • @AsteroidsWithWings I meant something else. I was arguing that this answer is wrong, because `int x[2][2] = {{0.f, 0.f}, {0.f, 0.f}};` is ill-formed, even though the answer claims those `0.f` are not at the "top level" because they're nested in braces. – HolyBlackCat Nov 11 '20 at 18:44
  • @HolyBlackCat That's fair – Asteroids With Wings Nov 11 '20 at 18:45
3

It is just an English phrase meant to be taken in context.

The rules of [dcl.init.list] tell us how an item in a list-initialiser cannot involve a narrowing conversion. Note 7 goes on to remind us of this, using the term "top-level" to describe those things that it's talking about (the immediate "members" of the list).

Example 14, which you are referring to, further includes an example of where a statement with a list-initialisation happens to contain a narrowing conversion, but the comment reminds us that this is fine because the conversion occurs at a "lower level" of nesting, again using the term "top-level" to describe the entities listed immediately in the initialiser list. No narrowing conversion was required to be applied to the actual list item.

There is no formal definition of the term "top-level"; it's supposed to be interpreted in its English sense while considering the possible nesting at play.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
1

I was confused about this a while back, but I think I understand this concept fairly well now.

Before I go to explain the concept, it must be noted that it is not the object that is top-level or low-level, but it is the "const" that is top-level or low-level depending it's position in the expression or context.

Top-level const means (that) "const" is making the object itself const (no matter what that object is pointing to).

Low-level const means (that) "const" is indicating that the object being referred(/pointed) to is const.

Ex.

const int i = 10; // const is indicating that i itself is const so it (const) is top level.

const int const *p = &i; // leftmost const is indicating that "pointed to" is const, so that const is low-level, other const in the expr is top-level.

Also, Top-level consts can appear with any obj type (Build in types, class type, pointer types), whereas Low-level const appears in base type of the compound types such as pointers & refs.

In pointers, both low-level and top-level can appear, but in refs, consts are always low-level, because refs are implicitly have top-level consts (once initialised, refs can't be bound to other objs, so in a sense they implicitly have top-level const on to them).

Hope that helps.

Onkar N Mahajan
  • 410
  • 3
  • 13
  • Ok, thanks. Pointer indirection is a further add-on to the answers above. The above discussions interpret top-level in the context of nesting. – Jerry Ajay Nov 12 '20 at 13:26