【CPP】函数重载、模版
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);
}
缺失默认参数
默认参数要从尾部开始定义
重复定义默认参数也会报错:
#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;
}
两个函数如果只是返回值不同,是不可以被重载的
#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;
}
参数可以匹配多个函数,有歧义,报错
#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.
进行了隐式类型转换
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
实例化
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
为什么还是调用了类型为double的函数?
为什么把函数删掉依然能够编译?
其实是因为函数可以做隐式实例化
Implicit Instantiation
- Implicit instantiation occurs when a function template is not explicitly instantiated
隐式实例化
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);
不合法的操作
专门去定义Point
结构体模版
#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)
如果只是template
是实例化,如果加了template<>
是特例化。
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 = &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
函数指针:指向函数的指针,要求指向的函数要跟指针的类型完全相同,也就是说指针指向的函数应该要有两个参数,且这两个参数的类型都应该是float
,返回值也是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 (&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