C++ type traits分析

C++ type traits分析

我們在平時常常會聽到有人說traits/萃取等高大上的東西,有時候可能也會對此產生很大的疑問,覺得type tratis很高大上,高深莫測;其實說到底這個東西很簡單,總結爲一句話就是在運行的時候識別類型(即類型萃取)。

本文我們大致看一下type traits的基本實現技術。

1. integral_constant

瞭解萃取機之前,我們先了解一下integral_constant, 這個在C++庫中定義爲一個常量的整數,定義如下:

template<class _Ty,
	_Ty _Val>
	struct integral_constant
	{	// convenient template for integral constant types
	static constexpr _Ty value = _Val;

	using value_type = _Ty;
	using type = integral_constant;

	constexpr operator value_type() const noexcept
		{	// return stored value
		return (value);
		}

	_NODISCARD constexpr value_type operator()() const noexcept
		{	// return stored value
		return (value);
		}
	};

這個的主要核心是定義了一個靜態常量值:static constexpr _Ty value = _Val;.

爲什麼需要定義這樣一個東西呢?我們不直接使用_Ty value = _Val定義一個全局的變量不是挺好的嘛,爲啥需要搞的那麼麻煩呢?

主要原因是:爲了C++編譯的時候能夠使用模板初編譯來確定其中的值。

integral_constant引申出來了兩個東西:

  1. true_type
  2. false_type

這兩個東西分別代表TRUE 和 FALSE,如下:

template<bool _Val>
	using bool_constant = integral_constant<bool, _Val>;

using true_type = bool_constant<true>;
using false_type = bool_constant<false>;

2. C++庫的type traits

2.1 Primary type categories

在這裏插入圖片描述

2.2 Composite type categories

在這裏插入圖片描述

2.3 Type properties

在這裏插入圖片描述

2.4 Type features

在這裏插入圖片描述

2.5 Type relationships

在這裏插入圖片描述

2.6 Property queries

在這裏插入圖片描述

2.7 Type transformations

在這裏插入圖片描述

3. type traits的例子


class CData1
{
public:
	CData1() {}
	virtual ~CData1() {}
};

class CData2
{
public:
	CData2() {}
	~CData2() {}
};

class CData3
{
public:
	int a;
	int b;
	int c;
};
int main(int args, char* argv[])
{
	std::cout << "CData1 has_virtual_destructor : " << std::has_virtual_destructor<CData1>::value << std::endl;
	std::cout << "CData2 has_virtual_destructor : " << std::has_virtual_destructor<CData2>::value << std::endl;
	std::cout << "CData3 has_virtual_destructor : " << std::has_virtual_destructor<CData3>::value << std::endl;
	std::cout << "CData1 is_pod : " << std::is_pod<CData1>::value << std::endl;
	std::cout << "CData2 is_pod : " << std::is_pod<CData2>::value << std::endl;
	std::cout << "CData3 is_pod : " << std::is_pod<CData3>::value << std::endl;
	return 0;
}

輸出結果如下:

CData1 has_virtual_destructor : 1
CData2 has_virtual_destructor : 0
CData3 has_virtual_destructor : 0
CData1 is_pod : 0
CData2 is_pod : 0
CData3 is_pod : 1

從上面我們可以看到type traits是非常厲害的,他能夠在編譯器的時候知道C++定義類型的所有屬性。

4. type tratis的實現

我們看幾個例子來大致看一下type traits的實現原理.

4.1 std::is_integral

std::is_integral用來判斷一個類型是否是整數,這個的實現原理如下:

	// STRUCT TEMPLATE _Is_integral
template<class _Ty>
	struct _Is_integral
		: false_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<bool>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<char>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<unsigned char>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<signed char>
		: true_type
	{	// determine whether _Ty is integral
	};

 #ifdef _NATIVE_WCHAR_T_DEFINED
template<>
	struct _Is_integral<wchar_t>
		: true_type
	{	// determine whether _Ty is integral
	};
 #endif /* _NATIVE_WCHAR_T_DEFINED */

template<>
	struct _Is_integral<char16_t>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<char32_t>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<unsigned short>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<short>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<unsigned int>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<int>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<unsigned long>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<long>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<unsigned long long>
		: true_type
	{	// determine whether _Ty is integral
	};

template<>
	struct _Is_integral<long long>
		: true_type
	{	// determine whether _Ty is integral
	};

	// STRUCT TEMPLATE is_integral
template<class _Ty>
	struct is_integral
		: _Is_integral<remove_cv_t<_Ty>>::type
	{	// determine whether _Ty is integral
	};

首先定義了一個template<class _Ty> struct _Is_integral : false_type 通用的模板,這個模板中有一個bool value = false的靜態成員。

然後就是真的所有的整數類型,創建特化模塊,例如如下:

template<>
	struct _Is_integral<int>
		: true_type
	{	// determine whether _Ty is integral
	};

這個模板中有一個bool value = true的靜態成員.

從這裏大致我們可以看出type traits是使用特化來確定特定的情況。

4.2 std::is_pod

對於簡單類型的判斷比較容易,我們實現所有類型的模板特化即可,但是對於類複雜類型的判斷,就比較麻煩了,C++標準庫的實現如下:

template<class _Ty>
	struct is_pod
		: bool_constant<__is_pod(_Ty)>
	{	// determine whether _Ty is a POD type
	};

template<class _Ty>
	_INLINE_VAR constexpr bool is_pod_v = __is_pod(_Ty);

	// STRUCT TEMPLATE is_empty
template<class _Ty>
	struct is_empty
		: bool_constant<__is_empty(_Ty)>
	{	// determine whether _Ty is an empty class
	};

template<class _Ty>
	_INLINE_VAR constexpr bool is_empty_v = __is_empty(_Ty);

	// STRUCT TEMPLATE is_polymorphic
template<class _Ty>
	struct is_polymorphic
		: bool_constant<__is_polymorphic(_Ty)>
	{	// determine whether _Ty is a polymorphic type
	};

對於__is_podC++標準庫並沒有公開的代碼,這裏也不知道具體如何實現,跟編譯器的底層實現細節有關,但是從我們所有的type traits來說,這個功能還是十分強大的。

5. iterator_traits

在萃取中,存在一個比較重要的萃取,如果上面的is_class, is_pod都沒有用過的話,那麼iterator_traits這個萃取機肯定是用過的,例如:

  template<typename _InputIterator, typename _Size, typename _ForwardIterator>
    inline _ForwardIterator
    uninitialized_copy_n(_InputIterator __first, _Size __n,
                         _ForwardIterator __result)
    { return std::__uninitialized_copy_n(__first, __n, __result,
                                         std::__iterator_category(__first)); }

其中std::__iterator_category(__first))這個就是類型萃取機,這個實現如下:

 template<typename _Iter>
    inline _GLIBCXX_CONSTEXPR
    typename iterator_traits<_Iter>::iterator_category
    __iterator_category(const _Iter&)
    { return typename iterator_traits<_Iter>::iterator_category(); }

iterator_traits 這個就是迭代器的萃取機,這個萃取機可以做如下事情:

  1. 萃取迭代器的類型。
  2. 萃取迭代器代表的值的類型。
  3. 萃取迭代器使用值的引用指針等類型。

這個迭代器實現如下:

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

  /// Partial specialization for pointer types.
  template<typename _Tp>
    struct iterator_traits<_Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef _Tp*                        pointer;
      typedef _Tp&                        reference;
    };

  /// Partial specialization for const pointer types.
  template<typename _Tp>
    struct iterator_traits<const _Tp*>
    {
      typedef random_access_iterator_tag iterator_category;
      typedef _Tp                         value_type;
      typedef ptrdiff_t                   difference_type;
      typedef const _Tp*                  pointer;
      typedef ptrdiff_t                   difference_type;
      typedef const _Tp*                  pointer;
      typedef const _Tp&                  reference;
    };

對於我們STL的迭代器,都需要定義這些類型:

  1. Iterator::iterator_category 迭代器類型。
  2. Iterator::value_type : 迭代器的值類型。
  3. Iterator::difference_type : 迭代器的距離信息。
  4. Iterator::pointer : 迭代器指針。
  5. Iterator::reference : 迭代器的引用。

STL的迭代器其實就是模擬指針來實現的,所以指針,應該天生適合最合適的迭代器,因此給_Tp*const _Tp* 定義了特殊的萃取類型。

6. 總結

從上面分析,對於C++庫,萃取的實現一般都是定義模板來實現,對於普通的類型,匹配這個模板的定義;然後針對特殊類型實現特化模板支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章