M. Winter
M. Winter

Reputation: 426

Using std::initializer_list[] as const list of lists of varying length

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

Answers (2)

jvstech
jvstech

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

n. m. could be an AI
n. m. could be an AI

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

Related Questions