1-Default Arguments
Default arguments
- A feature in C++ (not C)
- To call a function without providing one or more trailing arguments
default-argument.cpp
#include <iostream> #include <cmath> using namespace std; float norm(float x, float y, float z); float norm(float x, float y, float z = 0); float norm(float x, float y = 0, float z); int main() {<!-- --> cout << norm(3.0f) << endl; cout << norm(3.0f, 4.0f) << endl; cout << norm(3.0f, 4.0f, 5.0f) << endl; return 0; } float norm(float x, float y, float z) {<!-- --> return sqrt(x * x + y * y + z * z); }
#include <iostream> #include <cmath> using namespace std; float norm(float x, float y, float z); float norm(float x, float y = 0, float z); int main() {<!-- --> return 0; } float norm(float x, float y, float z) {<!-- --> return sqrt(x * x + y * y + z * z); }
Missing default parameters
Default parameters should be defined starting from the end
Repeatedly defining default parameters will also report an error:
#include <iostream> #include <cmath> using namespace std; float norm(float x, float y, float z); float norm(float x, float y, float z = 0); float norm(float x, float y = 0, float z = 4); int main() {<!-- --> return 0; } float norm(float x, float y, float z) {<!-- --> return sqrt(x * x + y * y + z * z); }
2-Function-Overloading
Why to overload?
- C99
<math.h> double round (double x); float roundf (float x); long double roundl (long double x);
- C++11
<cmath> double round (double x); float round (float x); long double round (long double x);
- which one do you prefer?
Function overloading
- Which function to choose? The compiler will perform name lookup
- Argument-dependent lookup, also known as ADL
- The return type will not be considered in name lookup
#include <iostream> using namespace std; int sum(int x, int y) {<!-- --> cout << "sum(int, int) is called" << endl; return x + y; } float sum(float x, float y) {<!-- --> cout << "sum(float, float) is called" << endl; return x + y; } double sum(double x, double y) {<!-- --> cout << "sum(double, double) is called" << endl; return x + y; } // //Is the following definition correct? // double sum(int x, int y) // {<!-- --> // cout << "sum(int, int) is called" << endl; // return x + y; // } int main() {<!-- --> cout << "sum = " << sum(1, 2) << endl; cout << "sum = " << sum(1.1f, 2.2f) << endl; cout << "sum = " << sum(1.1, 2.2) << endl; //which function will be called? //cout << "sum = " << sum(1, 2.2) << endl; return 0; }
//Is the following definition correct? double sum(int x, int y) {<!-- --> cout << "sum(int, int) is called" << endl; return x + y; }
Two functions cannot be overloaded if they only have different return values.
#include <iostream> using namespace std; int sum(int x, int y) {<!-- --> cout << "sum(int, int) is called" << endl; return x + y; } float sum(float x, float y) {<!-- --> cout << "sum(float, float) is called" << endl; return x + y; } double sum(double x, double y) {<!-- --> cout << "sum(double, double) is called" << endl; return x + y; } int main() {<!-- --> //which function will be called? cout << "sum = " << sum(1, 2.2) << endl; return 0; }
Parameters can match multiple functions. If there is ambiguity, an error will be reported.
#include <iostream> using namespace std; int sum(int x, int y) {<!-- --> cout << "sum(int, int) is called" << endl; return x + y; } int main() {<!-- --> //which function will be called? cout << "sum = " << sum(1, 2.2) << endl; return 0; }
warning: implicit conversion from 'double' to 'int' changes value from 2.2 to 2 [-Wliteral-conversion] cout << "sum = " << sum(1, 2.2) << endl; ~~~ ^~~ 1 warning generated.
implicit type conversion
3-Function Templates
Why function templates
- The definitions of some overloaded functions may be similar
Explicit Instantiation
- A function template is not a type, or a function, or any other entity
- No code is generated from a source file that contains only template definitions
- The template arguments must be determined, then the compiler can generate an actual function
Instantiate
template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } // Explicitly instantiate template double sum<double>(double, double); // instantiates sum<char>(char, char), template argument deduced template char sum<>(char, char); // instantiates sum<int>(int, int), template argument deduced template int sum(int, int);
template1.cpp
#include <iostream> #include <typeinfo> using namespace std; template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } // Explicitly instantiate template double sum<double>(double, double); int main() {<!-- --> auto val = sum(4.1, 5.2); cout << val << endl; return 0; }
The input type is d 9.3
#include <iostream> #include <typeinfo> using namespace std; template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } // Explicitly instantiate template float sum<float>(float, float); int main() {<!-- --> auto val = sum(4.1, 5.2); cout << val << endl; return 0; }
The input type is d 9.3
Why is the function of type double still called?
Why can it still compile if I delete the function?
In fact, it is because functions can be instantiated implicitly
Implicit Instantiation
- Implicit instantiation occurs when a function template is not explicitly instantiated
implicit instantiation
template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } // Implicitly instantiates product<int>(int, int) cout << "sum = " << sum<int>(2.2f, 3.0f) << endl; // Implicitly instantiates product<float>(float, float) cout << "sum = " << sum(2.2f, 3.0f) << endl;
template2.cpp
#include <iostream> #include <typeinfo> using namespace std; template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } int main() {<!-- --> // Implicitly instantiates product<int>(int, int) cout << "sum = " << sum<int>(2.2f, 3.0f) << endl; // Implicitly instantiates product<float>(float, float) cout << "sum = " << sum(2.2f, 3.0f) << endl; return 0; }
sum = The input type is i 5 sum = The input type is f 5.2
Function template specialization
- We have a function template
template<typename T> T sum(T x, T y)
- If the input type is Point
struct Point {<!-- --> int x; int y; };
- But no + operator for
Point;
- We need to give a special definition for this case
specialization.cpp
#include <iostream> #include <typeinfo> using namespace std; template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } struct Point {<!-- --> int x; int y; }; int main() {<!-- --> //Explicit instantiated functions cout << "sum = " << sum(1, 2) << endl; cout << "sum = " << sum(1.1, 2.2) << endl; Point pt1 {<!-- -->1, 2}; Point pt2 {<!-- -->2, 3}; Point pt = sum(pt1, pt2); cout << "pt = (" << pt.x << ", " << pt.y << ")" << endl; return 0; }
specialization.cpp:9:14: error: invalid operands to binary expression ('Point' and 'Point') return x + y; ~ ^ ~ specialization.cpp:38:16: note: in instantiation of function template specialization 'sum<Point>' requested here Point pt = sum(pt1, pt2);
Illegal operation
Specifically define the Point
structure template
#include <iostream> #include <typeinfo> using namespace std; template<typename T> T sum(T x, T y) {<!-- --> cout << "The input type is " << typeid(T).name() << endl; return x + y; } struct Point {<!-- --> int x; int y; }; // Specialization for Point + Point operation template<> Point sum<Point>(Point pt1, Point pt2) {<!-- --> cout << "The input type is " << typeid(pt1).name() << endl; Point pt; pt.x = pt1.x + pt2.x; pt.y = pt1.y + pt2.y; return pt; } int main() {<!-- --> //Explicit instantiated functions cout << "sum = " << sum(1, 2) << endl; cout << "sum = " << sum(1.1, 2.2) << endl; Point pt1 {<!-- -->1, 2}; Point pt2 {<!-- -->2, 3}; Point pt = sum(pt1, pt2); cout << "pt = (" << pt.x << ", " << pt.y << ")" << endl; return 0; }
sum = The input type is i 3 sum = The input type is d 3.3 The input type is 5Point pt = (3, 5)
If only template
is instantiated, if template<>
is added, it is specialization.
4- Function Pointers and References
Function pointers
norm_ptr
is a pointer, a function pointer- The function should have two
float
parameters, and returnsfloat
#include <iostream> #include <cmath> using namespace std; float norm_l1(float x, float y); //declaration float norm_l2(float x, float y); //declaration float (*norm_ptr)(float x, float y); //norm_ptr is a function pointer int main() {<!-- --> norm_ptr = norm_l1; //Pointer norm_ptr is pointing to norm_l1 cout << "L1 norm of (-3, 4) = " << norm_ptr(-3.0f, 4.0f) << endl; norm_ptr = & amp;norm_l2; //Pointer norm_ptr is pointing to norm_l2 cout << "L2 norm of (-3, 4) = " << (*norm_ptr)(-3.0f, 4.0f) << endl; return 0; } float norm_l1(float x, float y) {<!-- --> return fabs(x) + fabs(y); } float norm_l2(float x, float y) {<!-- --> return sqrt(x * x + y * y); }
L1 norm of (-3, 4) = 7 L2 norm of (-3, 4) = 5
Function pointer: A pointer to a function. The function pointed to must be of the same type as the pointer. That is to say, the function pointed to by the pointer should have two parameters, and the types of both parameters should be float
, the return value is also float
- A function pointer can be an argument and pass to a function
<stdlib.h> void qsort(void *ptr, size_t count, size_t size, int(*comp)(const void*, const void*));
- To sort some customized types, such as
struct Point struct Person
Function references
function-reference.cpp
#include <iostream> #include <cmath> using namespace std; float norm_l1(float x, float y); //declaration float norm_l2(float x, float y); //declaration float ( & amp;norm_ref)(float x, float y) = norm_l1; //norm_ref is a function reference int main() {<!-- --> cout << "L1 norm of (-3, 4) = " << norm_ref(-3, 4) << endl; return 0; } float norm_l1(float x, float y) {<!-- --> return fabs(x) + fabs(y); } float norm_l2(float x, float y) {<!-- --> return sqrt(x * x + y * y); }
L1 norm of (-3, 4) = 7
5- Recursive Functions
Recursive Functions
- A simple example
recursion.cpp
#include <iostream> using namespace std; void div2(double val); int main() {<!-- --> div2(1024.); // call the recursive function return 0; } void div2(double val) {<!-- --> cout << "Entering val = " << val << endl; if (val > 1.0) div2( val / 2); // function calls itself else cout << "--------------------------" << endl; cout << "Leaving val = " << val << endl; }
Entering val = 1024 Entering val = 512 Entering val = 256 Entering val = 128 Entering val = 64 Entering val = 32 Entering val = 16 Entering val = 8 Entering val = 4 Entering val = 2 Entering val = 1 -------------------------- Leaving val = 1 Leaving val = 2 Leaving val = 4 Leaving val = 8 Leaving val = 16 Leaving val = 32 Leaving val = 64 Leaving val = 128 Leaving val = 256 Leaving val = 512 Leaving val = 1024
- Pros:
- Good at tree traversal
- Less lines of source code
- Cons:
- Consume more stack memory
- May be slow
- Difficult to implement and debug