vector
sAs with any class type, the vector
template controls how we define and initialize vector
s. Table 3.4 (p. 99) lists the most common ways to define vector
s.
We can default initialize a vector
(§ 2.2.1, p. 44), which creates an empty vector
of the specified type:
vector<string> svec; // default initialization; svec has no elements
It might seem that an empty vector
would be of little use. However, as we’ll see shortly, we can (efficiently) add elements to a vector
at run time. Indeed, the most common way of using vector
s is to define an initially empty vector
to which elements are added as their values become known at run time.
We can also supply initial value(s) for the element(s) when we define a vector
. For example, we can copy elements from another vector
. When we copy a vector
, each element in the new vector
is a copy of the corresponding element in the original vector
. The two vector
s must be the same type:
vector<int> ivec; // initially empty
// give ivec some values
vector<int> ivec2(ivec); // copy elements of ivec into ivec2
vector<int> ivec3 = ivec; // copy elements of ivec into ivec3
vector<string> svec(ivec2); // error: svec holds strings, not ints
vector
Another way to provide element values, is that under the new standard, we can list initialize (§ 2.2.1, p. 43) a vector
from a list of zero or more initial element values enclosed in curly braces:
vector<string> articles = {"a", "an", "the"};
The resulting vector
has three elements; the first holds the string "a"
, the second holds "an"
, and the last is "the"
.
As we’ve seen, C++ provides several forms of initialization (§ 2.2.1, p. 43). In many, but not all, cases we can use these forms of initialization interchangably. So far, we have seen two examples where the form of initialization matters: when we use the copy initialization form (i.e., when we use =
) (§ 3.2.1, p. 84), we can supply only a single initializer; and when we supply an in-class initializer (§ 2.6.1, p. 73), we must either use copy initialization or use curly braces. A third restriction is that we can supply a list of element values only by using list initialization in which the initializers are enclosed in curly braces. We cannot supply a list of initializers using parentheses:
vector<string> v1{"a", "an", "the"}; // list initialization
vector<string> v2("a", "an", "the"); // error
We can also initialize a vector
from a count and an element value. The count determines how many elements the vector
will have; the value provides the initial value for each of those elements:
vector<int> ivec(10, -1); // ten int elements, each initialized to -1
vector<string> svec(10, "hi!"); // ten strings; each element is "hi!"
We can usually omit the value and supply only a size. In this case the library creates a value-initialized element initializer for us. This library-generated value is used to initialize each element in the container. The value of the element initializer depends on the type of the elements stored in the vector
.
If the vector
holds elements of a built-in type, such as int
, then the element initializer has a value of 0. If the elements are of a class type, such as string
, then the element initializer is itself default initialized:
vector<int> ivec(10); // ten elements, each initialized to 0
vector<string> svec(10); // ten elements, each an empty string
There are two restrictions on this form of initialization: The first restriction is that some classes require that we always supply an explicit initializer (§ 2.2.1, p. 44). If our vector
holds objects of a type that we cannot default initialize, then we must supply an initial element value; it is not possible to create vector
s of such types by supplying only a size.
The second restriction is that when we supply an element count without also supplying an initial value, we must use the direct form of initialization:
vector<int> vi = 10; // error: must use direct initialization to supply a size
Here we are using 10 to instruct vector
how to create the vector
—we want a vector
with ten value-initialized elements. We are not “copying” 10 into the vector
. Hence, we cannot use the copy form of initialization. We’ll see more about how this restriction works in § 7.5.4 (p. 296).
In a few cases, what initialization means depends upon whether we use curly braces or parentheses to pass the initializer(s). For example, when we initialize a vector<int>
from a single int
value, that value might represent the vector
’s size or it might be an element value. Similarly, if we supply exactly two int
values, those values could be a size and an initial value, or they could be values for a two-element vector
. We specify which meaning we intend by whether we use curly braces or parentheses:
vector<int> v1(10); // v1 has ten elements with value 0
vector<int> v2{10}; // v2 has one element with value 10
vector<int> v3(10, 1); // v3 has ten elements with value 1
vector<int> v4{10, 1}; // v4 has two elements with values 10 and 1
When we use parentheses, we are saying that the values we supply are to be used to construct the object. Thus, v1
and v3
use their initializers to determine the vector
’s size, and its size and element values, respectively.
When we use curly braces, {...}
, we’re saying that, if possible, we want to list initialize the object. That is, if there is a way to use the values inside the curly braces as a list of element initializers, the class will do so. Only if it is not possible to list initialize the object will the other ways to initialize the object be considered. The values we supply when we initialize v2
and v4
can be used as element values. These objects are list initialized; the resulting vector
s have one and two elements, respectively.
On the other hand, if we use braces and there is no way to use the initializers to list initialize the object, then those values will be used to construct the object. For example, to list initialize a vector
of string
s, we must supply values that can be used as string
s. In this case, there is no confusion about whether to list initialize the elements or construct a vector
of the given size:
vector<string> v5{"hi"}; // list initialization: v5 has one element
vector<string> v6("hi"); // error: can't construct a vector from a string literal
vector<string> v7{10}; // v7 has ten default-initialized elements
vector<string> v8{10, "hi"}; // v8 has ten elements with value "hi"
Although we used braces on all but one of these definitions, only v5
is list initialized. In order to list initialize the vector
, the values inside braces must match the element type. We cannot use an int
to initialize a string
, so the initializers for v7
and v8
can’t be element initializers. If list initialization isn’t possible, the compiler looks for other ways to initialize the object from the given values.
Exercise 3.12: Which, if any, of the following vector
definitions are in error? For those that are legal, explain what the definition does. For those that are not legal, explain why they are illegal.
(a) vector<vector<int>> ivec;
(b) vector<string> svec = ivec;
(c) vector<string> svec(10, "null");
Exercise 3.13: How many elements are there in each of the following vector
s? What are the values of the elements?
(a) vector<int> v1;
(b) vector<int> v2(10);
(c) vector<int> v3(10, 42);
(d) vector<int> v4{10};
(e) vector<int> v5{10, 42};
(f) vector<string> v6{10};
(g) vector<string> v7{10, "hi"};