Array of arrays initialized as const float* instead of const float **

Viewed 653

I came across this code

float range[] = { 0, 256 };
const float* histRange = { range };

For me, { range } means 'array of arrays' since the variable range is an array of float. Therefore, for me, it would make more sense.

const float** histRange = { range };

But the compiler (VC++ from VS2019) doesn't like it (expects a const float*)

Could someone explain to me why {range} is not an 'array of arrays' (float**), but instead just a regular float pointer?

Interesting fact: When I do

float range[] = { 0, 256 };
auto aa = { range };
auto bb = range;
std::cout << typeid(aa).name() << std::endl;
std::cout << typeid(bb).name() << std::endl;

the output is (in VC++ VS2019)

std::initializer_list<float * __ptr64>
float * __ptr64
1 Answers

Text enclosed by braces is not an expression, and doesn't have a type. A braced list is only allowed in certain contexts, and in each case the language definition specifies the semantics of the larger context featuring that braced list.

In the line:

const float* histRange = { range };

we are initializing a scalar, not an array. The [] syntax is required to declare an array. It is not defining an unnamed array and having histRange point to the first element of that array.

The meaning of a braced list as initializer for a scalar (other than auto) is that the list must contain 0 or 1 elements; and if it contains 1 element then the behaviour is the same as if the braces were omitted.

Or in other words, initializers for scalars can optionally have braces, e.g. int x = { 5 };. This rule has been around since C.

A braced list with more than one element would not be allowed in this context (although gcc accepts it with a warning by default, and ignores any list elements besides the first).

So, const float* histRange = range; is allowed because range is an array, and there is an implicit conversion from array to pointer-to-first-element-of-that-array, which has type float *, and there is also implicit conversion from float * to const float *.


const float** histRange = range; would be an error (with or without optional braces) because there is no conversion from float * to const float **.


Also allowed would be const float *histArr[1] = { range }; which is an array of pointers to const float and length 1; here you could have a braced list and a longer length.


In auto aa = { range }; , the use of auto has its own rules of type deduction, and the rule is that this syntax (without * or [] in the declarator) deduces aa as having type std::initializer_list<T> of one element.

Note that it would still not be correct to say that { range } has a type. The std::initializer_list<T> is initialized by the elements of the braced list.

This rule is slightly controversial; in C++11 auto aa{range}; was the same, but in C++17 it was changed to deduce aa to have the same type as range.

Related