Is it legal to declare a constexpr initializer_list object?
Asked Answered
R

1

31

As a question that came up during the discussion of this SO question:

Is it legal, maybe with N3471, to declare a constexpr std::initializer_list object? Example:

constexpr std::initializer_list<int> my_list{};

Why I think it may not be legal: initializer_list would have to be a literal type; but are there any guarantees that it is a literal type?

Citations from N3485.

[dcl.constexpr]/9:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized.

literal types requirements, [basic.types]/10, sub-bullet class types:

  • a class type (Clause 9) that has all of the following properties:
    • it has a trivial destructor,
    • every constructor call and full-expression in the brace-or-equal-initializers for non-static data members (if any) is a constant expression (5.19),
    • it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and
    • all of its non-static data members and base classes are of non-volatile literal types.

Bonus points ;) for answering if

constexpr std::initializer_list<int> my_list = {1,2,3,4,5};

is legal (with references). Though I think this is covered by the above + [dcl.init.list]/5

Reaction answered 17/4, 2013 at 14:43 Comment(2)
Just to repeat what I said in the chat: I don't really get the point of making std::initializer_list functions constexpr (N3741) if we can't legally write the last example.Herringbone
Another problem is the initialization. Common implementation uses pointers to access its underlying temporary array. If the array does not have static storage duration, initialization of the pointers makes the whole initialization not a constant expression. However, the storage duration of a temporary seems not to be defined yet.Dunnite
R
13

Update: The situation got a bit more complicated after the resolution of CWG DR 1684 removed the requirement quoted below. Some more information can be found in this discussion on the std-discussion mailing list and in the related question Why isn't `std::initializer_list` defined as a literal type?


[decl.constexpr]/8:

A constexpr specifier for a non-static member function that is not a constructor declares that member function to be const (9.3.1). [...] The class of which that function is a member shall be a literal type (3.9).

Therefore, the changes of N3471 guarantee std::initializer_list will be a literal type.


Note the constexpr ctor alone doesn't require std::initializer_list to be a literal type, see [dcl.constexpr]/4+8. Side note: An object of non-literal type with constexpr ctor can be initialized during constant initialization [basic.start.init]/2] (part of static initialization, performed before any dynamic initialization).

Reaction answered 17/4, 2013 at 18:25 Comment(6)
I found this answer some hours after posting the question - wondering what use would be a constexpr ctor in a non-literal type.Reaction
I'm not sure to understand everything—is constexpr std::initializer_list<int> my_list = {1,2,3,4,5}; legal or not in C++14?Gladwin
@Gladwin Sorry, it seems this requires a new investigation: The resolution of CWG DR 1684 removed the quoted passage from the C++14 Standard. I cannot find any other hints that std::initializer_list is required to be a literal type at the moment.Reaction
Thanks for the heads up!—these spec papers are still very cryptic to me. Using my C++14 + Clang 3.5 environment, I can see that all the methods from std::initializer_list are constexpr, so I thought the definition above had to be legal somehow, but it complains about the literal type thing so I wasn't sure if it was a compiler bug or something else. That being said, I agree with Morwenn—I don't see the point of all those constexpr if we can't even initialize the object... sounds like an half-baked class to me. Maybe I'll post a question to see if someone knows where it's heading to.Gladwin
@Gladwin It is possible though that completely removing the part about "shall be a literal type" is an oversight. I just confirmed that clang++ trunk and g++ trunk both still require that the class is a literal type if it has non-static constexpr member functions. (So that's either a bug in both compilers, or something wrong with the Standard.) -- I have also no idea what constexpr member functions on non-literal types could be good for.Reaction
Thanks for checking this out! I've posted a question here, let's see.Gladwin

© 2022 - 2024 — McMap. All rights reserved.