Reputation: 426
I need a static const list of lists of varying length (of QPointF
from Qt) that I directly initialize in my code (see below), and which is only used read-only later on.
I first used const std::vector<QPointF>[]
for that, but then it ocured to me that I could just use const std::initializer_list<QPointF>[]
instead (is the const
even necessary?).
static const std::initializer_list<QPointF> points[6] = {
{ { 0.50f, 0.50f } },
{ { 0.25f, 0.25f }, { 0.75f, 0.75f } },
{ { 0.25f, 0.25f }, { 0.50f, 0.50f }, { 0.75f, 0.75f } },
{ { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } },
{ { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.50f, 0.50f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } },
{ { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.25f, 0.50f }, { 0.75f, 0.50f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } },
};
Is this a valid use of std::initializer_list
? It looks like a hack to me, probably because the name of std::iniatilizer_list
suggests a certain use. Answers to this post also mention that initializer lists are intended to be used for, well, initialization of e.g. containers.
I also considered const std::array<...>[]
, but then all the inner lists have to be of the same length.
If std::initializer_list
is not the right choice here, then how to do this correctly. The alternative std::vector
does not feel right because of the heap allocation.
Upvotes: 2
Views: 574
Reputation: 864
Knowing your compiler output can help you with this decision. std::initializer_list<>
is just a thin wrapper around an array of objects.
If you really need static constant data, std::initializer_list<>[]
is perfectly valid (as long as you don't mind using its accessing syntax instead of subscript operators like you would find on std::array<>
or std::vector<>
). Clang 8 lowers each initializer_list
in the array to its own fixed-length array of QPointF
structs. It then fills another array with references to the QPointF
arrays it created:
// pseudo-C++ from Clang's generated output
QPointF RT1[1] = { { 0.50f, 0.50f } };
QPointF RT2[2] = { { 0.25f, 0.25f }, { 0.75f, 0.75f } };
QPointF RT3[3] = { { 0.25f, 0.25f }, { 0.50f, 0.50f }, { 0.75f, 0.75f } };
QPointF RT4[4] = { { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } };
QPointF RT5[5] = { { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.50f, 0.50f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } };
QPointF RT6[6] = { { 0.25f, 0.25f }, { 0.75f, 0.75f }, { 0.25f, 0.50f }, { 0.75f, 0.50f }, { 0.25f, 0.75f }, { 0.75f, 0.25f } };
// Reference array:
QPointF* points[6] = { RT1, RT2, RT3, RT4, RT5, RT6 };
Accessing individual values from the list
(points[5].begin() + 3)->y
is lowered to essentially the equivalent of
points[5][3].y
// alternatively:
(*(*(points + 5) + 3)).y
// which is actually:
(*(RT5 + 3)).y
(Implementations of std::initializer_list<>::begin()
can differ.)
So if you don't mind hidden dereferences or different syntax styles, this is fine. (Although you would have dereferenecs occuring with arrays or vectors, too.)
Try it with different optimization levels and see the results: https://godbolt.org/z/Vwjbp2
Upvotes: 2
Reputation: 120049
The standard says
An object of type initializer_list provides access to an array of objects of type const E.
It does not suggests that such objects are to be used exclusively as temporaries for initialising other objects, though this is without a doubt the primary intended use. The standard actually provides an example of it being correctly used as a variable.
So I cannot see anything wrong with what you have done.
Upvotes: 0