Example: Printing a Vector¶
Introduction¶
C++ vectors don’t work with <<
by default
vector<double> temps{1, 2, 3};
cout << temp; // compiler error: << doesn't know
// how to print a vector
What We Want¶
lets print vectors using the C++ literal notation
vector<double> temps{1, 2, 3};
cout << temp; // prints "{1, 2, 3}"
The print Function¶
lets not worry about <<
and first just write a print
function
void print(const vector<double>& v) {
// ...
}
// ...
vector<double> v = {1, 2, 3}
print(v); // "{1, 2, 3}"
The print Function¶
lets think about the basic cases
the empty vector is “{}”
a vector with 1 element has no commas, e.g. “{5}”
a vector with n elements has n - 1 commas, e.g. {1, 2, 3}
The print Function¶
this suggests we structure print
like this
void print(const vector<double>& v) {
int n = v.size();
if (n == 0) {
// ...
} else if (n == 1) {
// ...
} else {
// ...
}
}
The print Function¶
the first two cases are easy
void print(const vector<double>& v) {
int n = v.size();
if (n == 0) {
cout << "{}";
} else if (n == 1) {
cout << '{' << v[0] << '}';
} else {
// ...
}
}
The print Function¶
for the general case it helps to look at a concrete example
the braces are easy, so lets not write those
1, 2, 3
3 numbers, 2 commas
“, ” before all the numbers but the first
The print Function¶
1, 2, 3
print the first number
for the rest of the numbers, print “, ” followed by the number
The print Function¶
we’re done (the first version)!
void print(const vector<double>& v) {
int n = v.size();
if (n == 0) {
cout << "{}";
} else if (n == 1) {
cout << '{' << v[0] << '}';
} else {
cout << '{' << v[0];
for(int i = 1; i < n; ++i) {
cout << ", " << v[i];
}
cout << '}';
}
}
// ...
vector<double> v = {1, 2, 3};
print(v);
The println Function¶
it’s convenient to also write a println
function
void println(const vector<double>& v) {
print(v);
cout << '\n';
}
A Small Change to print¶
the n = 1 case is handled by the general “else” case
void print(const vector<double>& v) {
int n = v.size();
if (n == 0) {
cout << "{}";
//} else if (n == 1) { // delete these
// cout << '{' << v[0] << '}'; // two lines
} else {
cout << '{' << v[0];
for(int i = 1; i < n; ++i) {
cout << ", " << v[i];
}
cout << '}';
}
}
this simplifies and shortens print
A Problem¶
print
only works with vector<double>
vector<string> intro = {"Once", "upon", "a", "time"};
print(intro); // compiler error: print only works with
// with vector<double>,
// but intro is vector<string>
A Mediocre Solution¶
we could just write a version of print
that takes a vector<double>
void print(const vector<string>& v) {
int n = v.size();
if (n == 0) {
cout << "{}";
} else {
cout << '{' << v[0];
for(int i = 1; i < n; ++i) {
cout << ", " << v[i];
}
cout << '}';
}
}
the only change here is in the header
vector<double>
is now vector<string>
everything else is the same
A Mediocre Solution¶
this works
vector<string> intro = {"Once", "upon", "a", "time"};
print(intro); // "{Once, upon, a, time}"
but you’ll need to do that for every type T
that you have a vector<T>
of
A Better Solution: Templates¶
C++ templates let you essentially pass types as parameters to functions (and classes)
template<class T>
void print(const vector<T>& v) {
// ... same as before ...
}
template<class T>
void println(const vector<T>& v) {
print(v);
cout << '\n';
}
this works with any vector
now we only need to write one copy of the print function
A Note on Templates¶
templates turn out to be one of the most powerful and useful features of C++
many recent language decisions are based on using templates in new ways
basic uses of templates (like print
) are straightforward, but there are
many details that arise in other cases that make them more complex
error messages involving templates are often long and very hard to understand
Using the operator<<¶
C++ lets us overload the <<
operator
ostream& operator<<(ostream& out, const vector<double>& v) {
// ...
return out;
}
the name is operator<<
ostream
is an output stream (such as cout
)
note the return type is ostream&
to avoid making a copy
the header must always have this same general structure
Using the operator<<¶
the body of operator<<
is nearly the same as print
the differences are to use out
instead of cout
and it must end with
return out
ostream& operator<<(ostream& out, const vector<double>& v) {
int n = v.size();
if (n == 0) {
out << "{}";
} else {
out << '{' << v[0];
for(int i = 1; i < n; ++i) {
out << ", " << v[i];
}
out << '}';
}
return out;
}
Using the operator<<¶
now we can easily print vector<double>
s
vector<double> v = {1, 2, 3};
cout << "v = " << v << endl;
Using the operator<<¶
finally, we replace vector<double>
with vector<T>
template
template<class T>
ostream& operator<<(ostream& out, const vector<T>& v) {
int n = v.size();
if (n == 0) {
out << "{}";
} else {
out << '{' << v[0];
for(int i = 1; i < n; ++i) {
out << ", " << v[i];
}
out << '}';
}
return out;
}
Using the operator<<¶
vector<double> v = {1, 2, 3};
vector<string> intro = {"Once", "upon", "a", "time"};
cout << " v = " << v
<< "intro = " << intro
<< endl;