自学内容网 自学内容网

初尝类型萃取--typename、模板偏特化、和traits之(三)类型萃取

目录

当模板遇到迭代器和指针

类型萃取是如何解决上述问题的


本文作者在看过公众号《C++学习与探索》的文章《【一分钟学习C++】萃取机制》后,对类型萃取(type_traits)有了初步的认识,所以写下这篇博客。《【一分钟学习C++】萃取机制》门槛较高,如果没有理解typename和模板偏特化,是难以理解该文意思的。本文以及前面的两篇文章(初尝类型萃取--typename、模板偏特化、和traits之一、二)目的是将萃取涉及的基本知识点,如typename、模板偏特化先讲清楚,然后通过讲解例子的方式,使得类型萃取能被更多读者理解。

当模板遇到迭代器和指针

看下面例子:

#include <iostream>

template <typename T>
class tmp
{
public:
    *T ret(T s) {//编译失败,用auto代替*T是可以的
        return *s;
    }
};
int main()
{
    int p = 1;
    tmp<int*> t;
    int i = t.ret(&p);
    std::cout << "i = "<<i<<"\n";

    return 0;
}

在C++标准里,你可以用*指针 的方式获取一个变量,但是不能用*类型。上面的例子使用*T是失败的。

同样,使用迭代器也有类似的问题:

#include <vector>
#include <iostream>

template <typename T>
class tmp
{
public:
    *T ret(T s) {//编译同样失败
        return *s;
    }
};
int main()
{
    std::vector<int> vec{ 0,1,2 };
    std::vector<int>::iterator itr = vec.begin();
    tmp< std::vector<int>::iterator> t;
    int i = t.ret(itr);
    std::cout << "i = "<<i<<"\n";

    return 0;
}

正如前面所说,在C++标准里,你可以用*指针 的方式获取一个变量,但是不能用*类型。我们可以用auto替代*T的写法。另外,我们也可以采用萃取技术。

类型萃取是如何解决上述问题的

类型萃取主要依赖两个技巧:

1 使用typename

2 模板偏特化

看下面的代码:

#include <type_traits>
#include <iostream>

//先准备非特化的模板
template <typename T>
struct itr_trait;
//特化模板
template <typename T>
struct itr_trait<T*>
{
    //using valuetype = T;//等效于下面的
    typedef typename T valuetype;
    itr_trait(T * t) {
        i = *t;
        s = t;
    }
    valuetype i;
    valuetype* s;

    valuetype getS() { return *s; }
};



int main()
{
    int a = 2;
    itr_trait<int *> t(&a);
    std::cout <<"i = "<<t.i << ",*s = "<<*t.s<<",t.s is int * "<<(std::is_same<int*, decltype(t.s)>::value ? std::string("true") : std::string("false")) << std::endl;
    std::cout << "getS() = " << t.getS() << std::endl;
    std::cin.get();

    return 0;
}

main函数中的itr_trait<int *>匹配了特化模板itr_trait<T *>。所以T = int。

由于typedef typename T valuetype这句,valuetype = T = int

由于valuetype * s这句,s的类型就是int *

getS()的返回值就是int。这样也就解决了文章开头* T编译失败的问题。

再来看看《【一分钟学习C++】萃取机制》里面更加复杂的示例:

// class type
template <class T>
struct iterator_traits {//用来处理迭代器的情况
    typedef typename T::value_type value_type;//迭代器T一定含有T::value_type这个类型
};
// 指针类型偏特化,用来处理指针的情况
template <class T>
struct iterator_traits<T*> {
    typedef T value_type;
};
// const指针类型偏特化,用来处理指针的情况
template <class T>
struct iterator_traits<const T*> {
    typedef T value_type;
};

template <class T>
typename iterator_traits<T>::value_type//这定义func函数的返回值类型
func(T iter) {
    std::cout << "normal version" << std::endl;
    return *iter;
}

根据cppreference的说法std::iterator一定含有value_type这个成员。

在下面的例子里,T = vector<int>::iterator。func(T iter)里面的T是迭代器(而不是指针) ,则模板匹配非特化的情况。此时,由于

typedef typename T::value_type value_type;

iterator_traits<T>::value_type就等于vector<int>::iterator的value_type(value_type一定是iterator的成员),也就是int

// class type
template <class T>
struct iterator_traits {//用来处理迭代器的情况
    typedef typename T::value_type value_type;//迭代器T一定含有T::value_type这个类型
};


template <class T>
typename iterator_traits<T>::value_type//这定义func函数的返回值类型
func(T iter) {
    std::cout << "normal version" << std::endl;
    return *iter;
}

vector<int> vec;
vector<int>::iterator itr = vec.begin();

所以func函数的返回值类型iterator_traits<T>::value_type一定是vector<int>::iterator的value_type,也就是int。

假如func(T iter)的T类型是指针int *,则非特化模板不能与之匹配,因为int *没有value_type成员,使得

typedef typename T::value_type value_type;

不成立。

这时,特化模板就起作用了。

// 指针类型偏特化,用来处理指针的情况
template <class T>
struct iterator_traits<T*> {
    typedef T value_type;
};
// const指针类型偏特化,用来处理指针的情况
template <class T>
struct iterator_traits<const T*> {
    typedef T value_type;
};

template <class T>
typename iterator_traits<T>::value_type//这定义func函数的返回值类型
func(T iter) {
    std::cout << "normal version" << std::endl;
    return *iter;
}

int w = 2;
func<int *>(&w);

 下面这句将强制给iterator_traits加上一个成员类型value_type,且value_type = T。


    typedef T value_type;

于是func模板展开成了下述形式:


iterator_traits<int*>::value_type//这定义func函数的返回值类型
func(int * iter) {
    std::cout << "normal version" << std::endl;
    return *iter;
}

匹配了下面的模板:

template <class T>
struct iterator_traits<T*> {
    typedef T value_type;
};
//匹配为:
struct iterator_traits<int *>{
    typedef int value_type;
};

故func函数返回值仍然是int。


原文地址:https://blog.csdn.net/liji_digital/article/details/142995933

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!