Sequence Constructors, The Secret Ingredient
Standard containers inherit their convenient initialization syntax from the newly added sequence constructors. A sequence constructor takes a single argument of type
std::initializer_list<T>, where
initializer_list<T> is a new template defined in the C++09 header
<initializer_list>. This template converts a sequence of values of type
T into an array of
T. That array (henceforth referred to as the
range) is then used to populate the sequence constructor's object. Here's an example of a class with a sequence constructor:
#include <initializer_list>
struct C
{
//initialize C with doubles
C(std::initializer_list<double>);
};
//usage
C c1={1.5,0.99,23.45,-1.87};
Before continuing with the implementation of MyVector's sequence constructor, an explanation of how initializer_list performs its magic may be helpful. It's much simpler than you think!
Inside initializer_list
Initializer_list has two constructors and three member functions that grant access to its
range:
template<typename T> class initializer_list
{
//private data members: a pointer + size, or two pointers
public:
//constructors:
constexpr initializer_list(const T*, const T*); //[first,last)
constexpr initializer_list(const T*, int);//[first,first+size)
//accessing the range
constexpr int size() const; //number of elements
constexpr const T* begin() const; //first element
constexpr const T* end() const; //one-past-the-last element
};
Put simply, initializer_list transforms a random number of values into a valid STL range by creating an array that contains copies of those values.
The Finishing Touches
Thus far, adding a sequence constructor to MyVector has involved the following:
- An initializer_list object silently intercepted an initializer list of the form ={5,6,7,8}; and transformed the values between the braces into a valid range.
- A sequence constructor initialized its object by accessing the range using the size(), begin() and end() member functions of initializer_list.
- Because initializer_list's member functions are declared constexpr, the compiler can optimize the sequence constructor considerably.
MyVector's sequence constructor ultimately looks like this:
template<class T> class MyVector
{
public:
//newly-added sequence constructor
MyVector(initializer_list<T> s)
{
n=s.size()
elements = new T[n];
std::copy(s.begin(),s.end(),elements);
}
//...
};
Having furnished MyVector with a sequence constructor, you can now initialize it as you would any standard C++09 container:
MyVector <int> vi={1,3,5,7,11};