C++编译期优化——类型特性

󰃭 2016-05-04

C++编译期优化——类型特性(type traits)


type traits是通过判定或者更改模板中数据类型在编译期的属性来达到优化目的的。

type traits定义

c++11标准中,type traits定义在头文件<type_traits>中。一般来说,type traits的实现方法大致都相同:多个模板中会各定义一个静态常量,在模板特例化时根据传入的参数类型选择相应模板进行特例,通过实例的结果我们可以知道特例的模板,进而知道类型符合的特性。例如:

template <typename T>
struct is_void {
	static const bool value = false;
}

template <>
struct is_void<void> {
	static const bool value = true;
}

在上例中,我们对void进行的是特例化,非void类型则是泛型。实际上,还有许多的类型特性对于特性化不完全,我们称之为偏特性化:

template <typename T>
struct is_pointer{
	static const bool value = false;
}

template <typename T>
struct is_pointer<T *> {
	static const bool value = true;
}

一般来说,满足重载函数的参数类型一般都可以用来做类型特性,比如:

template< class T > struct remove_pointer                    {typedef T type;};
template< class T > struct remove_pointer<T*>                {typedef T type;};
template< class T > struct remove_pointer<T* const>          {typedef T type;};
template< class T > struct remove_pointer<T* volatile>       {typedef T type;};
template< class T > struct remove_pointer<T* const volatile> {typedef T type;};

上例中针对指针的修饰符const,volatile都进行了偏特性化。下面是remove_pointer的一个应用。

template <class T>
using ptr = T *;
template <class T>
using c_ptr = T * const;
auto bSame = std::is_same<std::remove_pointer<ptr<int>>::type, std::remove_pointer<c_ptr<int>>::type>::value;//true
std::cout << std::boolalpha << bSame << std::endl;

甚至有时候,模板的偏特性化中模板的参数个数不需要与默认模板参数个数一致,比如:

template<class T>
struct remove_extent { typedef T type; };
 
template<class T>
struct remove_extent<T[]> { typedef T type; };
 
template<class T, std::size_t N>
struct remove_extent<T[N]> { typedef T type;};

type traits优化

根据type traits得到的结论,我们可以使让编译器选择合适的方法来进行编译操作,拷贝算法通常是一个比较经典的案例。

#include<iostream>
#include<type_traits>
#include<iterator>


//使用最优条件:指向同一类型的指针,并且具有默认构造的类型(可以被memcpy安全操作)
template <bool bOpt>
struct copier {
	template <typename T1, typename T2>
	static T2 do_copy(T1 first, T1 last, T2 out) {
		std::cout << "Do not use opt!" << std::endl;
		while (first != last) {
			*out = *first;
			first++;
			out++;
		}
		return out;
	}
};

template <>
struct copier<true> {
	template <typename T1, typename T2>
	static T2 *do_copy(T1 *first, T1 *last, T2 *out) {
		std::cout << "Use opt!" << std::endl;
		memcpy(out, first, (last - first) * sizeof(T2));
		return out + (last - first);
	}
};

template <typename T1, typename T2>
inline T2 copy(T1 first, T1 last, T2 out) {
	using pure_T1 = typename std::remove_cv<typename std::iterator_traits<T1>::value_type>::type;
	using pure_T2 = typename std::remove_cv<typename std::iterator_traits<T2>::value_type>::type;
	const bool bOpt = std::is_same<pure_T1, pure_T2>::value
		&& std::is_pointer<T1>::value
		&& std::is_pointer<T2>::value
		&& std::is_trivially_copy_assignable<pure_T1>::value;
	return copier<bOpt>::do_copy(first, last, out);
}

struct node {
	int value;
	node *next;

	node() = default;
	node &operator= (const node &var) {
		if (&var != this) {
			this->value = var.value;
			this->next = nullptr;
		}
		return *this;
	}
};

int main() {
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int b[10] = { 0 };
	node aa[5] = { { 1, nullptr }, { 2, nullptr }, { 3, nullptr }, { 4, nullptr }, { 5, nullptr } };
	node bb[5];
	auto c = copy(a, &a[5], b);
	for (auto i : { 0, 1, 2, 3, 4 }) {
		std::cout << "b[" << i << "] = " << b[i] << std::endl;
	}

	auto cc = copy(aa, &aa[5], bb); 
	for (auto i : { 0, 1, 2, 3, 4 }) {
		std::cout << "bb[" << i << "] = {" << bb[i].value << "," << bb[i].next << "}" << std::endl;
	}

	return 0;
}

注①:is_trivially_copy_assignable不被g++支持,本例程在vs2013下测试。

Section Description Status Comments
20.9.4.3 Type properties Partial Missing is_trivially_copyable, is_trivially_constructible, is_trivially_default_constructible, is_trivially_copy_constructible, is_trivially_move_constructible, is_trivially_assignable, is_trivially_default_assignable, is_trivially_copy_assignable, is_trivially_move_assignable

测试结果为:

Use opt!
b[0] = 1
b[1] = 2
b[2] = 3
b[3] = 4
b[4] = 5
Do not use opt!
bb[0] = {1,00000000}
bb[1] = {2,00000000}
bb[2] = {3,00000000}
bb[3] = {4,00000000}
bb[4] = {5,00000000}

以上是类型特性的简单介绍,关于c++11的其他特性将在之后的文章中介绍。