Operator Overloading&Templates
Operator Overloading&Templates
Operator Overloading -
You can assign a different meaning to the operator for a user-defined type by defining the function
with the appropriate arguments.
m1 + m2
You are not allowed to overload the operators for built-in types
If you define this function as a member function, the first argument is implicit
If LHS object is not of the class type, the operator function can't be a member function.
sometimes having to use public accessors to get at private data is inconvenient, or not right
e.g. maybe this is the only place you need to - why clutter things?
or data does need to be completely private and should NOT have any reader/writter functions
but in this particular case you need to be able to get to it from outside the class
Friend Functions
in a class declaration, you can declare a function to be a "friend" of a class - grant access to private
or data does need to be completely private and should NOT have any reader/writter functions
OperatorOverloading&Templates 2/4/16, 6:02:26 PM 2
but in this particular case you need to be able to get to it from outside the class
Friend Functions
in a class declaration, you can declare a function to be a "friend" of a class - grant access to private
members.
Friend classes
Its member functions have access to the private members of the class
How to do it:
os << m.nickels << " nickels, " << m.dimes << " dimes, "
return os;
Four points!
Digress a bit.
cout is an object from the class ostream, initialized at program start, to output to the console.
return os;
accepts a reference to an ostream object, and returns the reference, for two reasons
cascaded I/O
each << operates on an ostream lhs, other objecr rhs, returns the ostream, so next can work on
it.
reference because you don't want to copy the ostream object - lots of internal state information
would be lost -
"pass through" reference argument. Same object returnd (alias for it) as passed in.
In handout
Can overload the input operator the same way, but less common
cout << "Enter values for x, nickels, dimes, quarters, and i" << endl;
Basic Templates
Intro
C++ is a strongly typed language - there is strict set of rules on what types that variables can have,
and when one type can be used as another type.
my_int = my_double;
C++ is also statically typed - types of variables are known and fixed at compile time.
Compare to LISP
Lisp is a language that is dynamically typed; every "variable" can have any kind of value at all - numbers,
strings, lists, even code (since code is a list of expressions).
Every value is actually an object that carries its type with it - so at run time, every operator or function
knows what to do with it; it it turns out to be the wrong type, you get a run-time error
(defun example()
(let (x y z)
(setq x 5)
(print x)
(setq y 10)
(print y)
(setq z (+ x y))
(print z)
;;(setq z (append x y)) ;; comment out
(setq z (+ x y))
))
;output:
5
10
15
But this run-time checking can be very slow. Statically typed is faster
But strong and static typing has a serious pitfall - impossible to use the same code to work on
different types
swap(double_var1, double_var2);
But the fuction can't be called, because a reference to an int can't be set to refer to a double - same
concept as disallowed pointer conversions.
Have to write a different version of swap for every type - what a pain!
Code will be fast and efficient, but are we doomed to writing it out over and over again?
Concept of generic programming - writing code that applies to all kinds of types, and letting compiler
modify it as needed for the type we want.
you write the code using a template, and specifying a TYPE PARAMETER (one or more)
The compiler generates the appropriate code for the TYPE PARAMETER when it is needed
A recipe for the compiler to follow to generate some code for you.
Std. Lib. uses them very heavily - almost all templates, in fact
Function templates
When your code uses the function, the compiler generates the suitable definition of the function -
INSTANTIATING the template
Compiler deduces the relevant types from the type used in the arguments of the call
function templates can be useful - StdLib is full of them, for handy & often used things - later.
Template example
these days, new "typename" keyword often used instead of "class" in the template declaration header
the template parameter is the name of a type - always - and might not be a class type!
compiler must see the template definition first - before your use of it in code
If you write:
swapem(my_int1, my_int2);
If you write:
Advantage:
After compiler instantiates the template, subject to normal rules of compilation and execution: code must
be correct and make sense;
For example
swapem(thing1, thing2) would fail to compile as a result because the assignment statements would
be illegal
code example:
template <class T>
void swapem(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
class Thing {
public:
Thing(int i_, char c_) : i(i_), c(c_) {}
int i;
char c;
friend ostream& operator<< (ostream&, const Thing&);
private:
Thing& operator= (const Thing& rhs);
};
Thing(int i_, char c_) : i(i_), c(c_) {}
int i;
OperatorOverloading&Templates
char c; 2/4/16, 6:02:26 PM 9
friend ostream& operator<< (ostream&, const Thing&);
private:
Thing& operator= (const Thing& rhs);
};
int main(){
Thing thing1(1,'A'), thing2(2, 'B');
cout << "thing1: " << thing1 << ", thing2: " << thing2 << endl;
swap(thing1, thing2);
cout << "thing1: " << thing1 << ", thing2: " << thing2 << endl;
return 0;
}
some template error messages can be confusing, though - lots of room for improvement in current
compilers!
g++ is actually among the better ones - parse it apart patiently - it tells you everything
char * p1 = s1;
char * p2 = s2;
What rules does the compiler follow to instantiate vs. when to use other overloaded functions:
First, compiler looks for exact type match with non-template function
A function template that will call another function based on any number of arguments
int main()
{
callit(42);
callit(1, 2);
callit(1, 2.2); // int double is preferred overload
callit(1, string("Hello"));
callit(1, "Hello"); // string literal converts to string variable
callit(1, 2, 3.14, "Goodbye");
// callit(1, 2, 3); // compile fails because there is no matching
function
/* output:
#include <iostream>
using namespace std;
int main()
{
print(7.5, "hello", 42);
<2>7.5<1>hello<0>42
<3>hello<2>42<1>7.5<0>zap
*/
<3>7.5<2>hello<1>42$
<4>hello<3>42<2>7.5<1>zap$
*/
OperatorOverloading&Templates 2/4/16, 6:02:26 PM 15
Class templates
A class template is a class definition in which member variables have parameterized types
start with
class Thing {
int x;
double y;
void defrangulate() {/* incredibly complex code */}
};
use by:
compiler generates:
class Thing {
int x;
double y;
void defrangulate() {/* incredibly complex code */}
};
compiler generates:
OperatorOverloading&Templates
class Thing { 2/4/16, 6:02:26 PM 16
int x;
double y;
void defrangulate() {/* incredibly complex code */}
};
compiler generates:
class Thing {
String x;
Item y;
void defrangulate() {/* incredibly complex code */}
};
classname<typeparameter>
e.g. Ordered_list was originally a non-template class that was a smart array of ints
Ordered_list<int>
must use this name everywhere we would have used the plain name before.
Even for ordinary classes, you can have member functions that are template functions!
Member functions defined inside the class declaration - no problem, same as non-template classes
Simple example:
definition inside
definition outside:
void Thing<T>::foo() {
blah;
blah;
}
How about class templates that use other class templates : no problem:
How about default parameters for class member functions that are templated types? Can do:
How about member functions that have an additional template type parameter? Can do, just a nested
sort of template declaration:
Template Magic Trick #1 Using a function template to infer types in creating a class template
Suppose we have
We want to instantiate it as an unnamed object with int, double and initialize it, say to give it to another
function. Have to write:
Writing out the class instantiation parameters can be inconvenient, but can't be avoided with class
template - we have to specify the types. However, suppose we write the following function template:
Now we can create and initialize our template class object and let the compiler deduce what T1 and T2 are
from the function arguments:
foo(make_Thing(42, 3.14));
Common pattern in the Standard Library: a function template that uses type deduction of parameters to
instantiate and return a class template object - many facilities come in pairs of templates: the instantiating
function and the class object.
e.g.
std::make_pair<int_var, my_string> creates and returns std::pair<int, std::string> initialized with int_var
and my_string.
template<typename T>
int Thing<T>::counter = 0; // initialize it
There is a different static int counter for each instantiation of T ! - Shared with the same T Things, but not
shared between different T Things.
Compiler must see the complete template definition for every translation unit that makes use of the
template.
Compiler must see the complete template definition for every translation unit that makes use of the
template.
Standard Library - iostream is actually a monster set of templates - almost all of the I/O library is
actually being read in, in near source form
Why - makes it easy for the same code to be used for both normal and wide characters!
It is possible to separate code into .h and .cpp files, but is not done very often, and is not as flexible -
see Stroustrup p.696 ff
for example, put declaration in .h, function definitions in .cpp followed by explicit instantiations,
compile the .cpp along with all other .cpp.
I’ve done this: Is only a good solution when you know the possible instantiations in advance:
Future compilers may make it better - "export" keyword was supposed to help
However, compiler processes all of the code in the translation unit, then instantiates the templates, then
compiles those.
It usually reports errors at the point of instantiation, but it is happening after the non-template code has
been compiled.
Allows for use of incomplete types at the point of instantiation if they become complete types later in
the translation unit.
tempplate<typename T>
using Vector = std::vector<T>;
*/
template <typename T>
using myOL = Ordered_list<T>;
using Vector = std::vector<T>;
OperatorOverloading&Templates
/* template <typename 2/4/16,
T> 6:02:26 PM 20
typedef Ordered_list<T> myOL; // error typedef can't be a template
*/
template <typename T>
using myOL = Ordered_list<T>;
and somewhere in the middle of it you refer to "foo" that is in the type given by T
T::foo
What is foo? Compiler can't tell just from T::foo because it doesn't know what T is yet.
On certain occasions, the compiler will complain because of the ambiguity. Usually foo should be the
name of a type embedded in T (like a nested class or a typedef). Compilers used to just assume it, bu it
could be something else - like a static variable or a member function.
If the compiler is confused, and foo is the name of a type, you need to tell the compiler with the typename
keyword:
typename T::foo
Library implementers: Preventing code bloat for template classes containing pointers
Code bloat: every template instantiation is a complete copy of the code, differing only in the type
declarations.
Can’t do anything about this, but there is a special case for pointer types:
Instead of 3 copies differing only by pointer type, implement in terms of void*, with casts to/form the actual
type
How its done (sketch - many details and members left out) - provide these three templates in this order to
the compiler
template<typename T>
class Linked_list {
void insert(const T& datum);
class Iterator {
T& Iterator::operator* ()
{return node->datum;}
};
class Linked_list {
void insert(const T& datum);
OperatorOverloading&Templates 2/4/16, 6:02:26 PM 21
class Iterator {
T& Iterator::operator* ()
{return node->datum;}
};
private:
class Node {
T datum;
};
};
Then specialize the whole template for void* - compiler picks this for Linked_list<void*> instead of the
base template
template<>
class Linked_list<void*> {
class Iterator {
void*& operator* ()
{return node->datum;}
};
private:
class Node {
void* datum
};
};
Then partially specialize for prointer types, and implement in terms of the void* instantiation. with casts
to/from the T* type and void*
template<T*>
class Linked_list {
public:
void insert(T* datum)
{vplist.insert (static_cast<void*>(datum));
class Iterator {
T*& operator* ()
{ …
return static_cast<T*>the_datum // very sketched
}
private:
Linked_list<void*> vplist;
};
Note: If member functions are inline, the “delegation” of calls to vplist takes no run-time.
Advantages:
All Linked_lists of pointer type share the same run-time code (the Linked_list<void*>).
Less code bloat if you have a lot of containers of different pointer types.
Disadvantages:
Lots of code near-duplication (more or less whole template for void* specialization)
Less code bloat if 2/4/16,
OperatorOverloading&Templates you have a lot ofPM
6:02:26 containers of different pointer types. 22
Disadvantages:
Lots of code near-duplication (more or less whole template for void* specialization)
the casting to/from void* can get very complex when taking into account: const interators, const
containers, and that T might be a const type (e.g. T* is const char *).