您的位置:首页 > 编程语言

iterator与traits编程技巧

2014-10-19 22:31 281 查看
带着问题来学习

gcc的全局函数_Destroy代码如下(stl_construct.h中)

template<typename _ForwardIterator>  
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last)  
{  
    typedef typename iterator_traits<_ForwardIterator>::value_type _Value_type;  
    std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::__destroy(__first, __last);  
}
问题1:当_ForwardIterator为container的iterator时,iterator中的确有定义value_type,但是如果_ForwardIterator是int*等指针类型时,怎么得到int这个value_type呢?

问题2:当_Value_type有trivial destructor时(即为基本数据类型或用户自己没有定义析构函数的复杂数据类型),函数__has_trivial_destructor(_Value_type)返回true,否则返回false,该函数是如何实现的?

回答1

首先定义一个“泛化”的iterator_traits模版,当_ForwardIterator中有value_type型别时,直接定义value_type为_Value_type

template<class Iterator>
struct iterator_traits
{
	typedef typename Iterator::value_type value_type;
}
当_ForwardIterator中为int*型的指针时,我们定义一个偏特化的模板

template<class T>
struct iterator_traits<T*>
{
	typedef T value_type;
}
当_ForwardIterator中为const int*型的指针时,如果我们期望得到的value_type仍然是int,而不是const int,那么我们再定义一个偏特化的模版

template<class T>
struct iterator_traits<const T*>
{
	typedef T value_type;
}
当然,iterator_traits的内容不止上面定义的那些,完整定义应该是(gcc中完整的定义是在stl_iterator_base_types.h中)

gcc中forward_iterator_tag并没有继承output_iterator_tag

struct input_iterator_tag{ };
struct output_iterator_tag{ };
struct forward_iterator_tag : public input_iterator_tag, public output_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag{ };
struct random_access_iterator_tag : public bidirectional_iterator_tag{ };

template<class Iterator>
struct iterator_traits
{
	typedef typename Iterator::value_type value_type;
	typedef typename Iterator::difference_type difference_type;
	typedef typename Iterator::pointer pointer;
	typedef typename Iterator::reference reference;
	typedef typename Iterator::iterator_category iterator_category;
}

template<class T>
struct iterator_traits<T*>
{
	typedef T value_type;
	typedef ptrdiff_t difference_type;
	typedef T* pointer;
	typedef T& reference;
	typedef random_access_iterator_tag iterator_category;
}

template<class T>
struct iterator_traits<const T*>
{
	typedef T value_type;
	typedef ptrdiff_t difference_type;
	typedef const T* pointer;
	typedef const T& reference;
	typedef random_access_iterator_tag iterator_category;
}
回答2
__has_trivial_destructor()是一个接受类型为参数的函数,有点像sizeof()函数。这类函数不是用C/C++实现的,是编译器的实现语言实现并封装到库文件中的,然后对外提供了这个接口。sgi版的STL中还有很多类似的函数,如 __has_trivial_constructor(),__has_trivial_copy()。

当然,这些函数是sgi的,那么C++有没有一个标准的接口呢?在c++ reference中(http://en.cppreference.com/w/cpp/types),我们可以看到这些模版有
is_trivial,is_trivially_constructible等。 这些模版是C++对外的接口,具体这些接口的用法与实现机制见本博客另一篇文章。

1、如何得到一个统一的traits接口?

要想得到一个统一的traits接口,其核心思想就是偏特化与显示特化,通过穷举模版的方法对外提供一个统一的接口。

偏特化

我们用iterator_traits<Iter>::value_type 来提取Iter中的value_type,普通情况下,Iter是一个composite类型并且当中有定义value_type。但是,当Iter是一个原生指针时,该语句也应该能返回指针指向的类型。于是,我们就多定义了两个(T*、const T*)偏特化的模版,当Iter是原生指针时,就匹配这两个模版当中的一个。

显示特化

假设在上面的_Destroy()函数中,没有编译没有提供__has_trivial_destructor()函数,而且我们规定,只有scalar类型(简单理解为基本数据类型)才has trivial destructor,那么我们应该怎么实现_Destroy()函数呢?

我们穷举所有scalar类型,为每一个scalar类型显示特化一个模版。

template<typename _ForwardIterator>  
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last)  
{  
    typedef typename iterator_traits<_ForwardIterator>::value_type _Value_type;  
    my_destroy_aux(__first, __last, scalar_type_traits<_Value_type>::scalar_type_or_non_scalar_type());  // (5)
} 

struct scalar_type{};
struct non_scalar_type{};

template<class Forward_Iterator>
void my_destory_aux(Forward_Iterator first, Forward_Iterator last, scalar_type) // (*)
{
	
}

template<class Forward_Iterator>
void my_destory_aux(Forward_Iterator first, Forward_Iterator last, non_scalar_type) // (**)
{
	typedef typename iterator_traits<Forward_Iterator>::value_type value_type;
	for(; first != last; first++)
		(&*first) -> ~value_type();	// or _Destroy(&*first);
}

template<class T>
struct scalar_type_traits
{
	typedef non_scalar_type scalar_type_or_non_scalar_type;
};

template<>
struct scalar_type_traits<int>
{
	typedef scalar_type scalar_type_or_non_scalar_type;
};

// here are more expclit specialization of type_traits<long> type_traits<char> etc
在第(5)行,当_Value_type为scalar类型时,则 my_scalar_type_traits<_Value_type>::scalar_type_or_non_scalar_type返回是scalar_type,后面加一对()表示实例一个匿名对象(因为函数的实参必须是对象,而不能是类型,除sizeof,__has_trivial_destructor等编译器提供的函数外),那么这个时候调用的就是(*)函数。

2、如何使用从统一的traits接口萃取出来的特性(萃取出来有可能是值,也有可能是类型)?

2.1 把if else中的多个函数设计成一个函数

例:STL之六 vector中的insert函数,函数代码如下

template<class T, class Allocator>  
template<class InputIterator>  
void Vector<T, Allocator>::insert(iterator pos, InputIterator first, InputIterator last)  
{  
    if(std::is_integral<InputIterator>::value)      
        insert(pos, (std::size_t)first, last);  
    else      
        insert_InputIterator(pos, first, last);      
}
if else中的两个insert函数具有同样多的参数,就算参数类型不一样也没关系,那么就可以把这两个函数统一成一个接口,去掉if else语句,编译的时候只编译其中的一个。做法是在这个两个函数的参数列表最后加一个各自加一个不同的类型,该类型编译时就必须能确定,这样编译的时候就能知道编译哪一个函数。由于if else中萃取的特性是值(true or false),那么就要利用true和false定义两个不同的类型。刚好std中有如下两个定义

typedef std::integral_constant<bool, true> true_type;

typedef std::integral_constant<bool, false> false_type;

true_type 与 false_type刚好有用到true 与 false,所以我们做如下的设计

template<class T, class Allocator>
template<class InputIterator>
void Vector<T, Allocator>::insert(iterator pos, InputIterator first, InputIterator last) // (1)
{	
	typedef std::integral_constant<bool, std::is_integral<InputIterator>::value> type;
	insert_aux(pos, first, last, type());	// (2)
}
template<class T, class Allocator>
void Vector<T, Allocator>::insert_aux(iterator pos, size_type count, const_reference value, std::true_type) // (3)
{
	// more code
}
template<class T, class Allocator>
template<class InputIterator>
void Vector<T, Allocator>::insert_aux(iterator pos, InputIterator first, InputIterator last, std::false_type) // (4)
{
	// more code
}
is_integral<InputIterator>::value要么是true,要么是false,即type要么是true_type,要么是false_type,那么第(2)行调用的要么是(3),要么是(4)。 当v.insert(v.begin(), 1, 2)匹配到(2)行的 insert(pos, first, last)时,此时InputIterator为int类型,那么 is_integral<int>::value 为 true,则type就是true_type。第(2)行的insert_aux(pos,
first, last, type())函数还是先匹配特化函数(3),其实没有”完美“匹配上,因为v.insert(v.begin(), 1, 2)中的1、2是int类型,优先级低于size_type(unsigned int),所以第(3)行会去匹配泛化函数(4),前三个参数类型”完美“匹配,但是最后一个类型没匹配上,此时第(2)行函数又会去匹配特化函数(3),通过隐式转换后发现能匹配上特化函数,则匹配成功。所以调用的是特化函数

programming skill:if else中的函数参数一定要一样多,它们的定义保持不变,只是形参列表最后多加一个类型。当萃取的是值时,要想办法用不同的值定义出不同的类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: