Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incomplete types and initializer_list

Tags:

c++

c++14

I am trying to model some meta data for serializing/de-serializing C++ objects. Here is something that captures the nuts & bolts of what I need; it compiles with GCC 5.2 (g++ sample.cpp -std=c++14) and with Clang 3.6 (clang++ sample.cpp -std=c++14).

My question is about the struct TypeInfo in the example. It contains an std::initializer_list of itself. Is this standards-conforming?

#include <cstdint>
#include <initializer_list>

enum class TypeCode : std::uint8_t { BOOLEAN, INT, OBJECT, STRING, SENTINEL };

struct TypeInfo
{
    TypeCode typeCode_;

    char fieldName_[64];

    union
    {
        std::uint16_t textMinLength_;
        std::uint16_t objectVersionMajor_;
    };

    union
    {
        std::uint16_t textMaxLength_;
        std::uint16_t objectVersionMinor_;
    };

    //  set only if typeCode_ = OBJECT
    std::initializer_list < TypeInfo > objectTypeInfos_;
};

int main()
{
    TypeInfo const sti { TypeCode::STRING, "updatedBy", { .textMinLength_ = 0 }, { .textMaxLength_ = 16 } };

    TypeInfo const iti { TypeCode::INT, "amount", { 0 }, { 0 } };

    TypeInfo const oti { TypeCode::OBJECT, "startTime", { .objectVersionMajor_ = 1 }, { .objectVersionMinor_ = 0 }, {
      TypeInfo { TypeCode::INT, "weekdays", { 0 }, { 0 } },
      TypeInfo { TypeCode::INT, "timeOfDay", { 0 }, { 0 } },
      TypeInfo { TypeCode::STRING, "timezone", { .textMinLength_ = 0 }, { .textMaxLength_ = 5 } }
    } };

    TypeInfo const noti { TypeCode::OBJECT, "schedule", { .objectVersionMajor_ = 1 }, { .objectVersionMinor_ = 0 }, {
      TypeInfo { TypeCode::INT, "id", { 0 }, { 0 } },
      TypeInfo { TypeCode::STRING, "description", { .textMinLength_ = 0 }, { .textMaxLength_ = 16 } },
      TypeInfo { TypeCode::OBJECT, "startTime", { .objectVersionMajor_ = 1 }, { .objectVersionMinor_ = 0 }, {
        TypeInfo { TypeCode::INT, "weekdays", { 0 }, { 0 } },
        TypeInfo { TypeCode::INT, "timeOfDay", { 0 }, { 0 } },
        TypeInfo { TypeCode::STRING, "timezone", { .textMinLength_ = 0 }, { .textMaxLength_ = 5 } }
      } }
    } };
}
like image 324
zrb Avatar asked Jan 07 '16 16:01

zrb


People also ask

What is initializer_list?

An object of type std::initializer_list<T> is a lightweight proxy object that provides access to an array of objects of type const T .

What is a initializer list in C++?

Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.


2 Answers

That actually induces undefined behavior with current wording. At the point of instantiation of std::initializer_list<TypeInfo>, TypeInfo is incomplete, hence [res.on.functions]/(2.5) applies:

In particular, the effects are undefined in the following cases:
(2.5) — if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

… and incomplete types are not specifically allowed for initializer_list yet - however, that's clearly defective. LWG issue 2493 opts to fix this:

The typical use-case of std::initializer_list<T> is for a pass-by-value parameter of T's constructor. However, this contravenes [res.on.functions]/2.5 because initializer_list doesn't specifically allow incomplete types (as do for example std::unique_ptr ([unique.ptr]/5) and std::enable_shared_from_this ([util.smartptr.enab]/2)).

A resolution would be to copy-paste the relevant text from such a paragraph.

I.e. your code is fine (and will be officially fine after resolution of the aforementioned DR).

like image 199
Columbo Avatar answered Nov 26 '22 09:11

Columbo


§ [res.on.functions]/2:

In particular, the effects are undefined in the following cases:
[...]
(2.5) - if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

I see no such specific allowance for initializer_list to be instantiated over an incomplete type (in either §[dcl.init.list] or §[support.init.list], at least as of N4296).

like image 22
Jerry Coffin Avatar answered Nov 26 '22 08:11

Jerry Coffin