Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Ashok Kumar, Bharath
so2021
Commits
650ec24a
Commit
650ec24a
authored
Jun 01, 2021
by
Praetorius, Simon
Browse files
Chapter about classes and constructors and generic programming
parent
2f88deb9
Changes
3
Hide whitespace changes
Inline
Side-by-side
presentation/_includes/classes.md
View file @
650ec24a
...
...
@@ -664,7 +664,7 @@ This is known as the **copy-and-swap ideom**.
-
The initializer list specifies how member variables are initialized before the body of the constructor is executed
-
Members should be initialized in the order of their definition
-
Members are initialized to their default value if not specified in the list
-
Members are initialized to their default value
/by their default constructor
if not specified in the list
-
`const`
member variables can only be initialized in the initializer list
Example
...
...
@@ -753,7 +753,7 @@ struct Vector {
double
norm
(
Vector
v
);
...
norm
(
10
);
// ERROR: Implicit conversion
norm
(
Vector
(
10
));
// OK, explicit constructor
static_cast
<
Vector
>
(
10
);
// OK
```
...
...
@@ -762,7 +762,7 @@ static_cast<Vector>(10); // OK
---
# Classes
## Application:
BLAS (Version 3)
Matrix type:
## Application:
A Dense
Matrix type:
```
c++
class
DenseMatrix
{
...
...
@@ -791,7 +791,7 @@ public:
---
# Classes
## Application:
BLAS (Version 3)
## Application:
A DenseMatrix type:
```
c++
void
mv
(
real_t
const
alpha
,
Vector
const
&
x
,
Vector
&
y
)
const
;
...
...
@@ -814,3 +814,111 @@ int main() {
M
.
mv
(
-
1.0
,
x
,
y
);
// y -= M*x
}
```
---
# Classes
## Other components of a class
### Associated Types
-
Types associated to a class type can be aliased inside the class definition
-
Can be used to collect common data types used in the class
-
Containers often specify its elements type as
`value_type`
and the type for
size and indices as
`size_type`
```
c++
struct
<
class_name
>
{
using
<
alias
>
=
<
type
>
;
typedef
<
type2
>
<
aias2
>
;
<
alias
>
variable
;
// variable declaration with the type
void
foo
(
<
alias
>
arg
);
// type in parameter list
};
using
T
=
<
class_name
>::<
alias
>
;
// extract the type from the class
```
---
# Classes
## Other components of a class
### Static data members
-
Variables associated not with an instance of a class but with the class type
-
Stored in a special section of memory with
*static lifetime*
-
Cannot be initialized by a constructor
-
Static data members (of integral/enum type) declared
`const`
can be initialized
inside the class
```
c++
struct
<
class_name
>
{
static
<
type
>
var
;
// declaration (uses 'static')
static
const
<
type2
>
var2
=
value
;
// initialization inside class
};
<
type
>
<
class_name
>::
var
=
value2
;
// definition (without 'static')
```
---
# Classes
## Other components of a class
### Static member functions
-
Function declared
`static`
inside the class are associated to the class type
-
Can be accessed using the name resolution operator
`::`
(even from outside the class)
-
On an instance static members can be accessed using the member access operator
`.`
```
c++
struct
<
class_name
>
{
static
<
type
>
var
;
static
<
return_type
>
<
function_name
>
(
<
args
>
...)
{
<
class_name
>::
var
=
value
;
// access of static data members
}
};
<
class_name
>::<
function_name
>
(
<
args
>
...);
// call a static function
```
---
# Classes
## Inheritance (simplified)
-
Classes can inherit the component of another class
-
Allows to split implementation into parts and compose a class by deriving from
other class(es)
-
Visibility restrictions by
`public`
,
`protected`
, and
`private`
```
c++
class
A
{
public:
int
var_a
;
double
fun_a
();
};
class
B
:
public
A
{
public:
int
var_b
;
double
fun_b
()
{
var_a
=
7
;
return
fun_a
();
}
};
```
---
# Classes
## Inheritance (simplified)
-
C++ allows multiple inheritance
-
Specify how inherited members are visible inside class
-
Can call constructors of inherited classes
```
c++
class
A
{
int
var_a
;
public:
A
(
int
a
)
:
var_a
(
a
)
{}
};
class
B
:
private
A
{
// members of A are private within B
public:
B
()
:
A
(
42
)
{}
// call constructor of base class
};
```
\ No newline at end of file
presentation/_includes/generic-programming.md
View file @
650ec24a
...
...
@@ -3,4 +3,549 @@ class: center, middle
# Generic Programming
---
# Generic Programming
\ No newline at end of file
# Generic Programming
Recall the example from the function section:
```
c++
int
square
(
int
const
x
)
{
return
x
*
x
;
}
float
square
(
float
const
x
)
{
return
x
*
x
;
}
double
square
(
double
const
x
)
{
return
x
*
x
;
}
```
-
Functions implementing the same algorithm should have the same name!
-
Same algorithm contains same code - repetition
-
General principle DRY:
**Don't repeat yourself**
--
-
Parametrize function with types (and values)
\(\t
o
\)
template of a function
```
c++
template
<
typename
T
>
T
square
(
T
const
x
)
{
return
x
*
x
;
}
```
---
# Generic Programming
## Function Templates
-
Instead of functions with explicit argument types, add (named) placeholder type,
called
*template parameters*
-
Template parameters introduced with
`class`
or
`typename`
```
c++
template
<
typename
T1
,
typename
T2
...>
<
return_type
>
<
function_name
>
(
<
args
>
...);
```
--
-
Function arguments and function body can use placeholder types like any other
specific type.
---
# Generic Programming
## Function Templates - Instantiation
-
Templates are not real functions! They must be
*instantiated*
with concrete types.
-
Template instiation either explicitly by
`function_name<T1,T2>(<args>...)`
or implicitly by "pattern matching" if argument list depends on all the types
`T1,T2...`
```
c++
int
main
()
{
square
<
int
>
(
42
);
// explicit template instantiation
square
<>
(
21
);
// implicit instantiation -> T = int
square
(
7
);
// implicit instantiation -> T = int
}
```
-
Implicit instantiation with angular brackets
`<>`
enforces a template instantiation
-
Implicit instantation is called
*argument type deduction*
(ATD)
---
# Generic Programming
## Function Templates
-
For multiple template parameters mixed explicit-implicit instantiation possible:
Specify types explicitly in order (from left to right), all non-specified types must
be deducible
-
Template parameter can have
*default values*
(like for function parameters)
```
c++
template
<
typename
T1
,
typename
T2
,
typename
T3
=
bool
>
void
template_function
(
T2
arg
)
{
T1
var1
=
7
;
// use template parameters like real types
T2
var2
=
arg
;
T3
var3
=
true
;
}
...
template_function
<
double
>
(
21
);
// T1 = double, T2 = int, T3 = bool
```
---
# Generic Programming
## Function Templates
-
If the type of the arguments is not explicitly needed, use
*abbreviated function templates*
, i.e.,
simple placeholder argument type
`auto`
```
c++
auto
square
(
auto
const
x
)
{
return
x
*
x
;
}
// [C++20]
...
square
(
42
);
// x has type int const
```
---
# Generic Programming
## Function Templates
### Constrained Template Parameters
-
Since C++20 the possible types of a function template can be constrained
-
Remember section about
*Constrained Placeholders*
-
Replace
`auto`
by
`<type_constraint> auto`
```
c++
auto
square
(
std
::
floating_point
auto
const
x
)
{
return
x
*
x
;
}
// function overload with different type constraint
auto
square
(
std
::
integral
auto
const
x
)
{
return
x
*
x
;
}
```
--
-
Also possible for named template parameters, replace
`typename T`
by
`<type_constraint> T`
```
c++
template
<
std
::
floating_point
T
>
auto
square
(
T
const
x
)
{
return
x
*
x
;
}
```
---
# Generic Programming
## Function Templates
### Function overloading
-
Overloading means: use the same name for functions with different
*signature*
-
Function templates have an extended signature, that includes the template
parameter list (number of parameters, type-constraints, data-type or value-type parameter)
-
Also included in the signature is the scope (namespace scope / class scope)
--
### Overload resolution - Which function is called?
1.
Find possible candidate functions with the correct name
2.
Argument type deduction for templates
3.
Remove function that do not fit (e.g. wrong number of arguments)
4.
Find best fitting function / most specialized/constrained function
---
# Generic Programming
## Function Templates
### Example
```
c++
template
<
class
T
>
// alternative syntax
T
square
(
T
const
x
)
{
return
x
*
x
;
}
// (1)
// non-template function
int
square
(
int
const
x
)
{
return
x
*
x
;
}
// (2)
```
--
### 1. Pass `int` to function:
```
c++
square
(
42
);
```
-
`(1)`
and
`(2)`
are found overloads
-
argument type deduction for
`(1)`
results in
`int`
-
non-template function preferred over template function
\(\t
o
\)
`(2)`
---
# Generic Programming
## Function Templates
### Example
```
c++
template
<
class
T
>
// alternative syntax
T
square
(
T
const
x
)
{
return
x
*
x
;
}
// (1)
// non-template function
int
square
(
int
const
x
)
{
return
x
*
x
;
}
// (2)
```
### 2. Pass `double` to function:
```
c++
square
(
42.0
);
```
-
`(1)`
and
`(2)`
are found overloads
-
argument type deduction for
`(1)`
results in
`double`
-
non-template function is not an
*exact match*
, i.e., need type conversion.
-
template function with
`T=double`
is better match
\(\t
o
\)
`(1)`
---
# Generic Programming
## Function Templates
Since function template are generated at compile
time, the full specification, i.e., function header and body, has to be
available whenever such a function is used. Therefore, template
functions must always be implemented in a
**header file**
.
> .h3[Remark:] If many template functions are used for many different
> datatypes, this significantly can bloat the resulting compiled
> program. Also, the compilation time can be much higher.
---
# Generic Programming
## Function Templates
If you want to split declaration and definition of a template function,
both parts must be put into a header file.
### Example
```
c++
#pragma once
// declaration of the function template
template
<
class
T
>
T
square
(
T
const
x
);
// definition of the function template
template
<
class
T
>
T
square
(
T
const
x
)
{
return
x
*
x
;
}
```
---
# Generic Programming
## Function Templates
If you want to split declaration and definition of a template function,
both parts must be put into a header file.
### Example
```
c++
#pragma once
// square.hh: declaration of the function template
template
<
class
T
>
T
square
(
T
const
x
);
#include
"square.impl.hh"
// include at the end of the file
```
Implementation file:
```
c++
#pragma once
// square.impl.hh: definition of the function template
template
<
class
T
>
T
square
(
T
const
x
)
{
return
x
*
x
;
}
```
---
# Generic Programming
## Class Templates
Similar to function templates, one can parametrize classes/structs with type parameters:
```
c++
template
<
typename
T1
,
typename
T2
...>
class
<
class_name
>
{
// examples of using the template types
using
value_type
=
T1
;
T1
var1
;
void
fun
(
T2
arg
)
{
...
};
};
```
### Example for a simple list:
```
c++
template
<
typename
T
>
struct
List
{
T
element
;
List
*
next
;
};
```
---
# Generic Programming
## Class Templates - Instantiation
Class templates are instantiated similar to function templates
1.
Explicit instantiation by specifying the types
2.
Implicit instantiation by
*class template argument deduction*
(CTAD)
### 1. Explicit instantiation
```
c++
int
main
()
{
List
<
int
>
ilist
;
List
<
double
>*
dlist
=
new
List
<
double
>
;
ilist
.
element
=
2
;
ilist
.
next
=
nullptr
;
dlist
->
element
=
2.0
;
dlist
->
next
=
nullptr
;
}
```
---
# Generic Programming
## Class Templates - Instantiation
Class templates are instantiated similar to function templates
1. Explicit instantiation by specifying the types
2. Implicit instantiation by *class template argument deduction* (CTAD)
### 2. Implicit instantiation
```
c++
template <class T>
struct Wrapper
{
T const& ref_;
// constructor depends on T
explicit Wrapper (T const& ref)
:
ref_(ref)
{}
}
;
int main () {
int var = 7;
Wrapper w{var}; // deduction of type T from constructor function
}
```
---
# Generic Programming
## Class Templates - Deduction guides
If the constructor does not directly depend on the template parameters, or is itself a template
one can write
*deduction guides*
to tell the compiler how to deduce the template parameters:
The constructor is thereby written as a template function with trailing return type:
```
c++
template
<
class
T1
...>
struct
class_name
{
class_name
(
<
args
>
...);
};
// deduction guide
template
<
class
T1
...>
class_name
(
<
args
>
...)
->
class_name
<
S1
,...
>
```
---
# Generic Programming
## Class Templates - Deduction guides
### Example
```
c++
template
<
class
T
,
class
Dummy
>
struct
Wrapper
{
T
const
&
ref_
;
// constructor depends on T
explicit
Wrapper
(
T
const
&
ref
)
:
ref_
(
ref
)
{}
};
```
Parameter
`Dummy`
cannot be deduced automatically.
Deduction Guide:
```
c++
template
<
class
T
>
Wrapper
(
T
const
&
)
->
Wrapper
<
T
,
void
>
;
```
---
# Generic Programming
## Class Templates - Nested Templates
The template parameter can be any concrete type. After instantiation, a class template gets a concrete
type. Thus it can be used inside templates:
```
c++
int
main
()
{
List
<
List
<
float
>
>
fllist
;
fllist
.
element
.
element
=
2.0
f
;
fllist
.
element
.
next
=
nullptr
;
fllist
.
next
=
nullptr
;
}
```
Here, the list can be extended in two dimensions, either by the
`next`
pointer or the
`element.next`
pointer.
---
# Generic Programming
## Function Templates and Class Templates
Function templates and class templates can be combined. When declaring a function template, the
arguments can be class templates or qualified types
```
c++
template
<
class
T
>
double
norm1
(
std
::
vector
<
T
>
const
&
vec
);
// accept all std::vector arguments only
template
<
class
T
>
double
norm2
(
T
const
&
arg
);
// accept any type and pass it by reference
template
<
class
T
>
double
norm3
(
T
const
arg
);
// accept any type and pass it by value
```
The parameter
`T`
is deduced depending on how it is called:
```
c++
std
::
vector
vec
{
1.0
,
2.0
,
3.0
};
// NOTE: CTAD happens here
norm1
(
vec
);
// => T = double
norm2
(
vec
);
// => T = std::vector<double>
norm3
(
vec
);
// => T = std::vector<double>
```
---
# Generic Programming
## Alias Templates
We have seen
*alias types*
or
*typedefs*
before. These can also be templated:
```
c++
template
<
typename
T1
,
typename
T2
...>
using
<
alias
>
=
class_name
<
T1
,
T2
...
>
;
```
--
## Dependent Names
Classes can have associated types as components. but also static functions, static members and a lot more.
When accessing associated types of a template parameter, we have to give the compiler a hint what we want
with the keyword
`typename`
:
```
c++
template
<
class
T
>
struct
Wrapper
{
using
value_type
=
typename
T
::
value_type
;
// `T::value_type` is dependent on `T`
};
```
---
# Generic Programming
## Alias Templates and Dependent Name
Alias templates are sometimes used to access common dependent types. The additional
`typename`
can
thereby be hidden in the alias:
```
c++
template
<
class
Container
>
using
value_t
=
typename
Container
::
value_type
;
```
--
On the other hand, if an alias template is part of a class, we might need to use an additional keyword
`template`
to access these types:
```
c++
struct
Wrapper
{
template
<
class
T
>
using
type
=
T
;
};
template
<
class
T
,
class
W
>
auto
foo
(
W
const
&
wrapper
)
->
typename
W
::
template
type
<
T
>
{
return
T
(
1
);
}
```
---