[C++ Primer] C16 一起粗略了解c++的类

C16 Templates and Generic Programming

16.1 Defining a Template

OOP vs generic programming:

OOP deals with types that are not known until run time; Whereas in generic programming, the type is determined during compilation.

Templates are the foundation of generic programming

Define a Template:

Define a function template. A function template is a formula from which we can generate type-specific versions of that function.

In a template definition, the template parameter list cannot be empty.

template <typename T>
T add(T a, T b) {
    return a+b;
}

class A{
    friend ostream& operator<<(ostream& os, const A& rhs);
public:
    A(int a): a_(a){};
    A operator+(A& rhs) {
        return this->a_ += rhs.a_;
    }
private:
    int a_;
};

ostream& operator<<(ostream& os, const A& rhs) {
    os << rhs.a_ << endl;
    return os;
}

int main() {

    string s1 = "hello ";
    string s2 = "world!";
    cout << add(1,2) << endl;
    cout << add(s1,s2) << endl;
    cout << add(1.0,2.0) << endl;
    A a1(1);
    A a2(5);
    cout << add(a1, a2) << endl;
    return 0;
}

3
hello world!
3
6

Class Templates

  1. 初始化时,一定要表明T是什么类型
  2. 对于在类外实现的成员函数,一定要加上type! 类内的就不用!
  3. 对于静态成员,每一种不同的type都共享同一个静态成员,但是不同type之间不共享
template<typename T>
class A{
public:
    T a;

    A& func() {
        cout << "1" << endl;
        return *this;
    }

    A& func2();

    static int count;
};

// 2. 不在类内实现函数,需要标T
template<typename T>
A<T>& A<T>::func2() {
    cout << "2" <<endl;
    return *this;
}

template<typename T>
int A<T>::count = 0;

int main() {
    // explicitly specify the value
    // 1. 初始化时,一定要表明T是什么类型
    A<int> a1;
    int c = a1.a;
    a1.func(); // 1
    a1.func2(); // 2

    // 3. 静态成员,每一个type的模板共享一个!
    A<long> a2;
    A<int> a3;
    a1.count += 1; // 1
    a2.count += 10; // 10
    cout << a1.count << endl; // 1
    cout << a2.count << endl; // 10
    cout << a3.count << endl; // 1
    return 0;
}

template parameter

// F -> default template argument!
template<typename T, typename F = std::greater<T>>
int compare(const T& a, const T& b, F f = F()) {
    if (f(a, b)) {
        return 1;
    }

    if (f(b, a)) {
        return -1;
    }

    return 0;
}

template<typename T>
bool isSmaller(const T& a, const T& b) {
    return a < b;
}

int main() {
    cout << compare(2,1) << endl; // 1, first T -> int
    cout << compare(1,2, isSmaller<int>) << endl; // 1
    return 0;
}

member template 模板嵌套

A class—either an ordinary class or a class template—may have a member function that is itself a template.

class A{
public:
    template<typename T>
    void func(T* p) {
        cout << "I will delete the input pointer" <<endl;
        delete p;
    }
};

int main() {
    A a;
    a.func(new int(10)); // I will delete the input pointer
    return 0;
}

16.2.5

Normal reference binding rules apply; and consts are low level, not top level.

const int* const a;
  |          |
 low        top

forward和右值

A function parameter, like any other variable, is an lvalue expression. 即函数的参数都默认视为左值

std::forward:

它通常用于实现完美转发(perfect forwarding).完美转发是指将函数参数以原样转发给另一个函数,包括值类别(左值或右值)和const限定符.

如果arg是右值,那么std::forward将返回一个右值引用;如果arg是左值,那么std::forward将返回一个左值引用;如果arg带有const限定符,那么std::forward将返回一个带有const限定符的引用


eg:
我们定义了三个f函数,分别接受一个左值引用、一个带有const限定符的左值引用和一个右值引用。然后我们定义了一个模板函数wrapper,它接受一个引用类型的参数x,并将其转发给f函数:

#include <utility>

void f(int& a) {
    cout << "f(int&): " << a << endl;
}

void f(const int& a) {
    cout << "f(const int&): " << a << endl;
}

void f(int&& a) {
    cout << "f(int&&): " << a << endl;
}

template<typename T>
void warrper(T&& x) {
    f(std::forward<T>(x));
}

int main() {
    int a = 10;
    const int b = 20;

    warrper(a);
    warrper(b);
    warrper(30); 
}

f(int&): 10
f(const int&): 20
f(int&&): 30

据实参的不同类型,wrapper函数会分别调用f(int&)、f(const int&)和f(int&&)
eg2:
template<typename T, typename t1, typename t2> 
void flip(T f, t1 a, t2 b) {
    f(b,a);
}

template<typename T, typename t1, typename t2> 
void flip1(T f, t1&& a, t2&& b) {
    f(b,a);
}

void func(int v1, int& v2) {
    cout << v1 << " " << ++v2 << endl;
}

// rvalue -> match any type of arguments(lvalue, rvalue)
int main() {
    int i = 10; // i is int&
    int j = 20; // j is int&
    flip(func, i, j); // 20 11
    
    cout << i << endl; // 10, i 并没有改变

    flip1(func, i, j); // 20 11

    cout << i << endl; // 11, i 改变, 因为右值转为引用
}

16.3 overloading an template

template<typename T>
void print(T arg) {
    cout << "I have only one parameter!" << endl;
    cout << arg << endl;
}

template<typename T, typename N>
void print(T arg1, N arg2) {
    cout << "I have two parameters!" << endl;
    cout << arg1 << ",";
    print(arg2);
}

int main() 
{
    int a = 1;
    string s = "five";
    print(a);
    print(a,s);
}

I have only one parameter!
1
I have two parameters!
1,I have only one parameter!
five

16.4 variadic templates可变参数模板

在上面的例子中,如果我有多个参数会怎么样?我要把overload函数按照不同的参数数目重写吗?不用!用typename… arg 即可,来看:

template<typename T>
void print(T arg) {
    cout << arg << endl;
}

template<typename T, typename... N>
void print(T arg1, N... arg2) {
    cout << arg1 << ",";
    print(arg2...);
}

int main() 
{
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    int e = 5;
    string s = "five";
    print(a,b,c,d,e);
    print(a,b,c,d,s);
}

print函数模板有两个版本:第一个版本只接受一个参数,并将其输出到控制台;第二个版本接受两个或更多参数,第一个参数将被输出到控制台,然后递归调用print函数模板,将其余的参数作为参数包传递给它,继续进行输出操作。这个递归过程会一直进行下去,直到参数包中没有元素为止

16.5 template Specializations 模板特化

在模板定义中,针对某个特定的类型或值,定义一个特殊的版本(或多个版本)来处理该类型或值。模板特化允许我们为特定的类型或值提供特殊的处理方式,使代码更加灵活和通用

eg: 如何为不同类型的参数提供不同的处理方式:

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void print(T arg) {
    cout << arg << endl;
}

// 特化版本1,用于处理字符串类型的参数
template<>
void print<string>(string arg) {
    cout << "The length of the string is " << arg.length() << endl;
    cout << arg << endl;
}

// 特化版本2,用于处理char类型的参数
template<>
void print<char>(char arg) {
    cout << "The ASCII code of the character is " << int(arg) << endl;
    cout << arg << endl;
}

int main() 
{
    int a = 1;
    double b = 3.14;
    string s = "hello";
    char c = 'A';
    
    print(a);
    print(b);
    print(s);
    print(c);
    
    return 0;
}

1
3.14
The length of the string is 5
hello
The ASCII code of the character is 65
A
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值