We showed you some basic C++ last semester, including using templates like Java uses generics. But that is far from all C++ templates can do… This lab will help you explore this topic in more detail.
We recommend that you work in pairs for this lab, like those before it.
1 Caveat
There exists a subset of programmers who believe that C++ templates beyond type generics are an abomination and should never be used. I hope this lab will help you make your own opinion, but be aware that if you work for a company that uses C++ and suggest some of these techniques, you might get some blow-back…
2 Review
Begin by reviewing the C++ lab from CSO1.
We’ll be picking up from where those left off in this lab.
3 A Mathematical Vector type
In mathematics, a vector has fixed length and adding vectors of different length is meaningless. In this lab you’ll use C++ templates to make a mathematical vector class that uses the type checker to ensure at compile time that no wrong-sized operations occur.
Implement a templated mathematical vector type in C++. When you are
done, the following code should do as the comments suggest (you’ll need
to comment out the does not compile
lines to get the others to
work):
typedef vec<double, 2> vd2;
typedef vec<double, 3> vd3;
typedef vec<int,4> vi4;
int main() {
;
vd3 astd::cout << a << std::endl; // prints (0, 0, 0)
[2] = 2.5;
astd::cout << a << std::endl; // prints (0, 0, 2.5)
std::cout << (a + a) << std::endl; // prints (0, 0, 5)
std::cout << a << std::endl; // prints (0, 0, 2.5)
;
vd2 bstd::cout << b << std::endl; // prints (0, 0)
;
vi4 cstd::cout << c << std::endl; // prints (0, 0, 0, 0)
std::cout << (a + b) << std::endl; // does not compile
std::cout << (a + c) << std::endl; // does not compile
[2] = 2.5; // implicitly casts a double to an int...
c[1] = 2.5;
b[2] = 2;
c
std::cout << b << std::endl; // prints (0, 2.5)
std::cout << c << std::endl; // prints (0, 0, 2, 0)
= {1.5, 2.5, 3.5};
vd3 x std::cout << x << std::endl; // prints (1.5, 2.5, 3.5)
= {1, 2, 3};
vd3 y std::cout << y << std::endl; // prints (1, 2, 3)
}
You code should not contain any heap-allocated memory (no
new
or malloc
)
When you’re done submit your C++ files to the submission site, OR show your TA what you’ve done for check-off.
4 Guides
Precede your
struct
(orclass
) withtemplate <typename N, int n>
so it will work with multiple types and lengths. (The only difference betweenstruct
andclass
in C++ is whether it defaults topublic
orprivate
.)Note that
template
classes and functions cannot be defined in separate.o
files. So, when template implementations are split across multiple files, usually the implementations are placed in header files.Use a static array
N data[n]
(or the like) as the only field so avoid heap memory allocationsConstructors have no return type and the same name as the
struct
- your default constructor should sets the data to all
0
s - the other should accept an
initializer list
, which is what the{1,2,3}
turns into at compile time#include <initializer_list>
- 1 argument, a
std::initializer_list<R>
whereR
is the type of value you expect to be passed in. - a
std::initializer_list<R>
is a collection, meaning you access it by iterator - you’ll need to verify that the initializer list has the right number
of values (it’s
size()
method should help) - we recommend using a template to pick the initializer list contained
type, e.g. with
template<typename R>
before the function
- your default constructor should sets the data to all
Operator overloading uses special function names
operator +
should accept only vectors of the same lengthoperator []
needs two variantsN& operator[] (int idx)
– allows setting by index by returning a referenceN operator[] (int idx) const
– allows reading when no reference exists
- Printing needs a special
friend
notation to let you access between theostream
andvec
classes:- a declaration like
friend std::ostream& operator << (std::ostream& out, const vec<N,n>& x)
placed in the class definition, indicating that theoperator<<
defined outside the class can access private member variables and function in the class - this function should use
out << thing
and thenreturn out
- a declaration like
It is best practice (but not technically required) to
put
template
declarations on the line before the struct or function they modifytemplate <typename R> (const R& t) { /*...*/ } R dostuff
use
const
for all arguments you will not modifydouble sqrt(const double x)
use reference types
const mytype&
forstruct
arguments you don’t want to copydouble length(const vec<double,3>& x)
use
const
for any method that does not modifythis
’s fieldsdouble length() const
5 C++ Iterators
Many STL structures in C++ use the iterator pattern to allow access. This has a somewhat different look than it does in Java or the like.
The collection offers two functions of note:
.begin()
returns an iterator pointing to the first element of the collection.end()
returns an iterator pointing to thepast the end
element of the collection
The iterator overloads three operators of note:
operator !=
tells if two iterators point to distinct entries in the collectionoperator *
gets the item pointed to out of the iteratoroperator ++
moves the item to the next spot in the collection
Thus, a loop that prints all items in a collection might look like
for(auto it = mycollection.begin(); // create an iterator
!= mycollection.end(); // and while it's not off the end
it ++it) { // move it forward
std::cout << *it << std::endl; // derreference to print
}
Note in the above that when you combine a declaration and
initialization, you can declare the type to be auto
meaning
use whatever type the initialization gives me
.