Why isn't `std::initializer_list` defined as a literal type?
C++Language LawyerC++14Initializer ListConstexprC++ Problem Overview
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?
C++ Solutions
Solution 1 - C++
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.
Solution 2 - C++
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.