Why isn't `std::initializer_list` defined as a literal type?
Asked Answered
L

2

67

This is a follow-up of this question: Is it legal to declare a constexpr initializer_list object?.

Since C++14, the std::initializer_list class has all of its methods marked with constexpr. It seems natural to be able to initialize an instance by doing constexpr std::initializer_list<int> list = {1, 2, 3}; but Clang 3.5 complains about list not being initialized by a constant expression. As dyp pointed out in a comment, any requirement for std::initializer_list to be a literal type seem to have vanished from the specs.

What's the point of having a class fully defined as constexpr if we can't even initialize it as such? Is it an oversight in the standard and will get fixed in the future?

Lanie answered 16/12, 2014 at 1:16 Comment(9)
Richard Smith seems to imply here that std::initializer_list has been made a literal type. However, I cannot find such a requirement in the Standard. A second question, which I've posted on a comment to my question linked above, is "May constexpr non-static member functions be declared as members of non-literal types?", see CWG DR 1684Leclair
This is weird, clang++ compiles this when you put it in the global scope: coliru.stacked-crooked.com/a/dab2834181fb8ea4 (This was clang bug 15117) Smells like another compiler bug to me.Leclair
clang++ also accepts it when it is a local static variable: coliru.stacked-crooked.com/a/700cf33e2446b63cLeclair
Related: groups.google.com/a/isocpp.org/d/topic/std-discussion/…Leclair
I've got difficulties to understand it all—is Richard Smith basically saying that constexpr std::initializer_list<int> list = {1, 2, 3}; should be legal? And hence it's a compiler bug as you're suspecting?Lanie
I don't quite understand it either. It seems to be implied in the discussion of this clang bug 15117, but I cannot deduce it from the Standard (drafts) or Richard Smith's hint at interpretation. See the continuing discussion on the std-discussion mailing list.Leclair
Related: groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/…Tail
"Is it an oversight in the standard and will get fixed in the future?" - True about C++14 in general.Progressist
Just for the record: GCC 4.9.1 is fine with this.Julian
K
8

The standard committee seems to intend on initializer_list being a literal type. However, it doesn't look like it's an explicit requirement, and seems to be a bug in the standard.

From § 3.9.10.5:

A type is a literal type if it is:
- a class type (Clause 9) that has all of the following properties:
- - it has a trivial destructor,
- - 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.

From § 18.9.1:

namespace std {
  template<class E> class initializer_list {
  public:
    /* code removed */
    constexpr initializer_list() noexcept;
    // No destructor given, so trivial
    /* code removed */
  };
}

This satisfies the first and second requirements.

For the third requirement though:

From § 18.9.2 (emphasis mine):

An object of type initializer_list<E> provides access to an array of objects of type const E. [Note: A pair of pointers or a pointer plus a length would be obvious representations for initializer_list. initializer_list is used to implement initializer lists as specified in 8.5.4. Copying an initializer list does not copy the underlying elements.
—end note]

So there is no requirement for the private members of the implementation of initializer_list to be non-volatile literal types; however, because they mention that they believe a pair of pointers or a pointer and a length would be the "obvious representation," they probably didn't consider that someone might put something non-literal in the members of initializer_list.

I'd say that it's both a bug in clang and the standard, probably.

Kerekes answered 23/1, 2015 at 18:2 Comment(0)
P
0

I remember shortly after C++11 came out I was creating an initializer_list whose individual members allocated dynamic memory, so they had non-trivial d'tors, therefore the initializer_list has a non-trivial d'tor. And there was some circumstance, I think I threw while the initializer_list was partially constructed, where the built part didn't get destroyed, causing a memory leak. I'm sure the compilers have been fixed by now.

Palaestra answered 15/10, 2020 at 23:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.