C++基础知识(4)模板与泛型编程

1. 模板基础

1.1 模板的介绍

(1)模板能实现其他语法难以实现的功能。
(2)模板分为类模板和函数模板,函数模板又分为普通函数模板和成员函数模板。

1.2 类模板基础

/*
* 在头文件XXX.hpp中定义模板
**/
#pragma once
template<typename T>
class MyArray
{
    using iterator = T*;
    using const_iterator = const T*;
public:
    MyArray(size_t count);
    ~MyArray();
    iterator begin() const;
    const_iterator cbegin() const;
private:
    T* data; // 要使用数组,最好使用头指针,因为C++智能指针对数组的支持并不好
};
// 最好都在类外定义函数,在类外定义的函数需要加上模板头
template<typename T>
MyArray<T>::MyArray(size_t count) 
{
    if (count)
    {
        data = new T[count]();
    }
 else
    {
        data = nullptr;
    }
}

template<typename T>
MyArray<T>::MyArray(size_t count) 
{
    if (count)
    {
        data = new T[count]();
    }
    else
    {
        data = nullptr;
    }
}

template<typename T>
MyArray<T>::~MyArray()
{
    if (data)
    {
        delete[] data;
    }
}

template<typename T>
// 重定义的东西需要typename关键字进行指定
// 这是因为::会判断iterator是否为成员变量或成员函数,而typename告诉编译器iterator是一个新的类型
typename MyArray<T>::iterator MyArray<T>::begin() const 
{
    return data;
}

template<typename T>
typename MyArray<T>::const_iterator MyArray<T>::cbegin() const
{
    return data;
}
/*
* 模板类的使用
**/
#include <iostream>
#include"myArray.hpp"
void testMian()
{
    MyArray<int> myArrayI(1);
    std::cout << *myArrayI.cbegin() << std::endl;
}

模板的实现原理:
模板需要编译两次,第一次只检查最基础的语法;当模板真正被调用时,比如把int带入T后,进行第二次编译,那时生成需要的类或函数。
不管是类模板还是函数模板的声明和定义都必须写在同一个文件中。

2. initializer_list与typename

2.1 initializer_list

initializer_list就是初始化列表,我们可以用初始化列表初始化各种容器,比如vector。
在MyArray模板中加入初始化列表的初始化。

#include<type_traits> // 类型萃取头文件
// 模板特化
template<typename T>
struct get_type
{
    using type = T;
};
template<typename T>
struct get_type<T*> 
{
    using type = T;
};
// 模板类
template<typename T>
class MyArray
{
    using iterator = T*;
    using const_iterator = const T*;
public:
    MyArray(size_t count);
    MyArray(const std::initializer_list<T>& list);
    MyArray(std::initializer_list<T>&& list);
    ~MyArray();
    iterator begin() const;
    const_iterator cbegin() const;
private:
    T* data;
};

template<typename T>
MyArray<T>::MyArray(const std::initializer_list<T>& list)
{
    if (list.size())
    {
        unsigned count = 0;
        data = new T[list.size()]();
        if (std::is_pointer<T>::value) // 萃取技术,判断类型是否为指针
        {
            for (auto elem : list)
            {
                data[count++] = new typename get_type<T>::type(*elem);
            }
        }
        else // 如果不为指针,直接浅拷贝
        {
            for (const auto& elem : list)
            {
                data[count++] = elem;
            }
        }
    }
    else
    {
        data = nullptr;
    }
}

template<typename T>
MyArray<T>::MyArray(std::initializer_list<T>&& list)
{
    if (list.size())
    {
        unsigned count = 0;
        data = new T[list.size()]();
        for (const auto& elem : list)
        {
            data[count++] = elem;
        }
    }
    else
    {
        data = nullptr;
    }
}

2.2 typename

  1. 在定义模板时表示这个是一个特定的类型。
  2. 在类外表明这是一个自定义类型。

3. 函数模板

3.1 普通函数模板

普通函数模板与类模板写法类似。

namespace mystd
{
    template<typename iter_type, typename func_type>
    void for_each(iter_type first, iter_type last, func_type func)
    {
        for (auto iter = first; iter != last; ++iter)
        {
            func(*iter);
        }
    }
}
void testMian()
{
    std::vector<int> ivec{ 1, 2, 3, 4, 5 };
    mystd::for_each(ivec.begin(), ivec.end(), [](int& elem) {
        ++(elem);
        });
}

3.2 类成员函数模板

template<typename T>
class MyVector
{
public:
    template<typename T2>
    void outPut(const T2& elem);
}
// 类成员函数的类外定义
template<typename T>
template<typename T2>
void MyVector<T>::outPut(const T2& elem)
{
    std::cout << elem << std::endl;
}

4. 默认模板参数

如果一个函数模板有了默认参数,之后该参数后续的所有都要加默认参数。

template<typename T, typename allocator_type = std::allocator<T>> // 默认模板参数
class MyVector
{
public:
    void MyVector<T, allocator_type>::outPut(T&& elem) // 都要加模板参数
    {
        std::cout << elem << std::endl;
    }
};

5. 模板特化

我们可以通过指定类型,来完成某些更精细的操作。
模板特化的应用:通过特化来获取类型

#include<type_traits> // 萃取技术头文件
template<typename T>
struct get_type
{
    using type = T;
};
template<typename T>
struct get_type<T*> 
{
    using type = T;
};
// 用来获取指针的特定类型
if (std::is_pointer<T>::value) // 萃取技术,判断类型是否为指针
{
    for (auto elem : list)
    {
        // get_type<T>::type 通过模板特化来获取指针的特定类型
        data[count++] = new typename get_type<T>::type(*elem); 
    }
}

注意:当模板函数需要特殊类型处理时就进行重载,当模板类需要特殊类型处理时就进行特化。

6. 万能引用

万能引用是C++中即能当左值,又能当右值的引用。

6.1 万能引用的格式:

(1)T&&(模板类型)

template<typename T> // 只有这个时候确定的T,才能是万能引用
void func(T&& parm) // 注意只有 T&& 是万能引用类型,如const T&&不是万能引用
// 当传入右值时,类型为:T&&
// 当传入左值时,类型为:T& && 引用折叠为 T&
template<typename T>
class MyVector
{
public:
    void MyVector<T>::outPut(T&& elem) // 此时T属于类中,在类构建时已经确定,因此不是万能引用
    {
        std::cout << elem << std::endl;
    }
};

(2)auto&&(auto类型)

auto&& elem = var; // auto&&为万能引用类型,同时const auto&&也不是万能引用

6.2 引用折叠

一个引用不是左值引用就是右值引用,当一个万能引用被认为是左值时,T&&类型应该是T& &&,此时类型会被折叠为T&

7. 完美转发

完美转发的问题,是为了解决万能引用之间的转发。

template<typename T>
void func1(T&& var)
{
    func2(var);
}
template<typename T>
void func2(T&& var)
{
    // 此时func2接收的值,不管是左值还是右值,都是左值
}

于是C++提供了forward模板解决上述问题。

template<typename T>
void func1(T&& var)
{
    func2(std::forward<T>(var)); // 完美转发为了配合万能引用,因此必须加上<T>
    // std::forward<T>(var) 会根据var的类型,自动推测左值或右值
}
template<typename T>
void func2(T&& var)
{
    // ......
}

8. 萃取技术

原理:通过模板特化的方式对类型进行判断,以达到类型萃取的目的。

struct TrueType
{
    static bool Get()
    {
        return true;
    }
};
struct FalseType
{
    static bool Get()
    {
        return false;
    }
};

template<class T>
struct MyTypeTraits
{
    typedef FalseType IsMyType;
};
// 通过模板特化将int判断出来
template<>
struct MyTypeTraits<int>
{
    typedef TrueType IsMyIntType;
};
/*
* 在函数中调用即能判断类型
**/
if (MyTypeTraits<T>::IsMyType::Get())

C++提供了相应的类型萃取头文件type_traits,以供我们进行类型判断。

#include<type_traits>
std::is_integral::value() // 判断是否为整形
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值