赞
踩
演示案例1(std::is_pointer<>)
#include <type_traits> template<typename T> void foo(const T& val) { if (std::is_pointer<T>::value) { std::cout << "foo() called for a pointer" << std::endl; } else { std::cout << "foo() called for a value" << std::endl; } } int main() { int num; int *p = # foo(num); foo(p); }
- 运行结果如下:
- 代码解释:
- 这里的std::is_pointer<>属于一个traits类。如果传入的参数类型为指针类型,其返回std::true_type;如果传入的参数类型不是指针类型,其返回std::false_type。然后std::true_type::value或std::false_type::value返回相对应的true或false
- 第一个foo()调用传入的参数为非指针类型
演示案例2(std::true_type、std::false_type)
- 我们修改演示案例1中的foo()函数,让其打印传入的元素的值,于是设计了下面的代码:
- 但是这是错误的,编译不通过
- 假设val不是指针类型,而代码中却有*val的操作,这显然是错误的
template<typename T> void foo(const T& val) { std::cout << (std::is_pointer<T>::value ? *val : val) << std::endl; }
- 如果想要达到上面的目的,可以借助std::true_type和std::false_type来完成。代码如下:
#include <type_traits> //如果是指针,调用这个 template<typename T> void foo_impl(const T& val,std::true_type) { std::cout << "foo() called for a pointer:" << *val << std::endl; } //如果不是指针,调用这个 template<typename T> void foo_impl(const T& val, std::false_type) { std::cout << "foo() called for a value:" << val << std::endl; } template<typename T> void foo(const T& val) { foo_impl(val, std::is_pointer<T>()); } int main() { int num = 10; int *p = # foo(num); foo(p); }
- 直接调用foo_impl()函数也是可以的。例如:
int main() { int num = 10; int *p = # foo_impl(num, std::is_pointer<int>()); foo_impl(p, std::is_pointer<int*>()); }
演示案例3(std::is_integral<>,针对整数类型的弹性重载)
- 例如现在我们有一批重载函数,一部分是针对于整数类型的,一部分是针对于浮点数类型的。例如:
void foo(short); void foo(unsigned short); void foo(int); void foo(float); void foo(double); void foo(long double);
- 上面的代码有很大的缺点:这样做重复工作很多,代码比较冗余,并且如果加入了新数据类型那么还需要重新定义新的foo()函数
- 一种做法是使用trait机制提供的模板类,例如此处使用std::integral<>模板。定义的代码如下:
//针对于整数类型设计的 template<typename T> void foo_impl(T val, std::true_type); //针对于浮点数类型设计的 template<typename T> void foo_impl(T val, std::false_type); template<typename T> void foo(T val) { //通过is_integral萃取类型 //如果T为整数类型,std::is_integral返回std::true_type;否则返回std::false_type foo_impl(val, std::is_integral<T>()); }
演示案例4(std::common_type<>,处理通用类型)
- 假设我们有个函数来比较两个值的最小值,并将最小值进行返回,如果T1和T2的数据类型不一致,那么返回值该如何定义哪?
- 我们可以借助std::common_type<>解决这个问题。例如:
- 如果传入的两个实参都是int,或传入的都是long,或者传入的一个是int一个是long,那么std::common_type<>返回int,因此下面的min的返回值为int类型
- 如果传入的参数一个是string而另一个是字符串字面常量,那么下面的min的返回值为std::string
template<typename T1, typename T2> typename std::common_type<T1, T2>::type min(const T1& x, const T2& y);
- 使用std::common_type<>的前提是,两个实参它们有共同的数据类型。前提是程序员自己保证的(看下面的实现原理)
- std::common_type<>的实现原理如下:
- 其内部使用?:运算符,直接返回T1的数据类型。因此上面不论min()的T2参数属于什么类型,其只返回T1所表示的数据类型
- 其内部使用了一个std::declval<>模板,特属于trait的一种,其根据传入的类型提供一个值,但不去核算它(最终返回一个该值的rvalue reference)
- 然后再使用decltype关键字导出表达式的类型
- 通过上面的comm_type<>就可以找出一个共同类型,如果找不到,可以使用common_type<>的重载版本(这正是chrono程序库的作为,使它得以结合duration;详情见后面的chrono程序库介绍)
①类型判断式
- 下图列出了针对于所有类型都使用的trait:
- 下图列出了针对于class类型都使用的trait
- std::true_type、std::false_type:
- 上面的类型的返回值是std::true_type或std::false_type
- std::true_type和std::false_type都是std::integral_constant的特化,因此它们相应的value成员可以返回true或false。如下图所示:
- 一些注意事项:
- bool和所有character类型(char、char16_t、char32_t、wchar_t)都属于整数类型
- std::nullptr_t为基础数据类型
- 上面大部分都是单参数形式的,但并非全部都是
- 一个“指向const类型”的非常量pointer或reference,其本身并不是一个常量(见下面演示案例)
- 用以检验copy和move语义的那些trait,只检验是否相应的表达式为可能。例如,一个“带有copy构造函数(接受常量实参)但没有move构造函数”的类型,仍然是move constructible
- is_nothrow..type trait特别被用来阐述noexcept异常声明
- 下面是is_const<>的演示案例:
std::cout << boolalpha; std::cout << "is_const<int>::value " << is_const<int>::value << std::endl; std::cout << "is_const<const volatile int>::value " << is_const<const volatile int>::value << std::endl; std::cout << "is_const<int* const>::value " << is_const<int* const>::value << std::endl; std::cout << "is_const<const int*>::value " << is_const<const int*>::value << std::endl; std::cout << "is_const<const int&>::value " << is_const<const int&>::value << std::endl; std::cout << "is_const<int[3]>::value " << is_const<int[3]>::value << std::endl; std::cout << "is_const<const int[3]>::value " << is_const<const int[3]>::value << std::endl; std::cout << "is_const<int[]>::value " << is_const<int[]>::value << std::endl; std::cout << "is_const<const int[]>::value " << is_const<const int[]>::value << std::endl;
②用以检验类型关系的Trait
- 下图列出的type trait可以检查类型之间的关系,包括检查class type提供了哪一种构造函数和哪一种赋值操作等等
- is_assignable<>的使用注意事项:
- 注意,基本数据类型(例如int)可以表现出lvalue或是rvalue,因此你不能够直接赋值,例如“42=77”,这是错误的。因此is_assignable<>第一个类型如果是一个nonclass类型,永远会获得false_type
- 如果是class类型,以其寻常类型作为第一类型是可以的,因为存在一个有趣的旧规则:你可以调用“类型为class”的rvalue的成员函数。例如:
std::cout << boolalpha; std::cout << "is_assignable<int,int>::value " << is_assignable<int, int>::value << std::endl; std::cout << "is_assignable<int&,int>::value " << is_assignable<int&, int>::value << std::endl; std::cout << "is_assignable<int&&,int>::value " << is_assignable<int&&, int>::value << std::endl; std::cout << "is_assignable<long&,int>::value " << is_assignable<long&, int>::value << std::endl; std::cout << "is_assignable<int&,void*>::value " << is_assignable<int&, void*>::value << std::endl; std::cout << "is_assignable<void*,int>::value " << is_assignable<void*, int>::value << std::endl; std::cout << "is_assignable<const char*,std::string>::value " << is_assignable<const char*, std::string>::value << std::endl; std::cout << "is_assignable<std::string,const char*>::value " << is_assignable<std::string, const char*>::value << std::endl;
- 下面是is_constructible<>的演示案例:
std::cout << boolalpha; std::cout << "is_constructible<int>::value " << is_constructible<int>::value << std::endl; std::cout << "is_constructible<int,int>::value " << is_constructible<int, int>::value << std::endl; std::cout << "is_constructible<long,int>::value " << is_constructible<long, int>::value << std::endl; std::cout << "is_constructible<int,void*>::value " << is_constructible<int, void*>::value << std::endl; std::cout << "is_constructible<void*,int>::value " << is_constructible<void*, int>::value << std::endl; std::cout << "is_constructible<const char*,std::string>::value " << is_constructible<const char*, std::string>::value << std::endl; std::cout << "is_constructible<std::string,const char*>::value " << is_constructible<std::string, const char*>::value << std::endl; std::cout << "is_constructible<std::string,const char*,int,int>::value " << is_constructible<std::string, const char*, int, int>::value << std::endl;
- std::use_allocator<>被定义在<memory>头文件中
③类型修饰符
- 下图列出的trait允许你改动类型
- 使用规则:
- 如果想要为某一类型添加一个属性,前提是该属性尚未存在
- 如果想要为某一类型移除一个属性,前提是该属性已经存在
- 下面是一些演示案例:
- 类型const int&可被降级或扩展。例如:
- 一些注意事项:
- 一个“指向某常量类型”的reference本身并不是常量,所以你不可以移除其常量性
- add_pointer<>必然包含使用remove_reference<>
- 然后make_signed<>和make_unsigned<>要求实参若非整数类型就必须是枚举类型,bool除外,所以如果你传入reference会导致不明确行为
- add_value_reference<>把一个rvalue reference转换为一个lvalue reference,然而add_rvalue_reference<>并不能把一个lvalue reference转换为一个rvalue reference(类型保持不变)。因此,必须这么做才能将一个lvalue转换为一个rvalue reference:
④其他trait
- 下图列出了其余所有type trait。它们用来查询特殊属性、检查类型关系、或提供更复杂的类型变换
- decay<>允许你讲“以by value传入”的类型T转换为其相应类型。以此方式,它转换array和function类型称为pointer,把lvalue转换为rvalue——其中包括移除const和volatile
- common_type<>为所有被传入的类型提供一个共同类型(它可以有1个、2个或更多个类型实参)
- 下面是一些演示案例:
- template<typename T>
- void foo(T val) { val++; }
-
- int main()
- {
- int num1 = 1, num2 = 1;
-
- foo(num1);
- std::cout << "num1:" << num1 << std::endl;
-
- foo(std::ref(num2)); //改为T&调用
- std::cout << "num2:" << num2 << std::endl;
- }
- int func(int x, int y)
- {
- return x + y;
- }
-
- int main()
- {
- std::vector<std::function<void(int, int)>> tasks;
- tasks.push_back(func);
- tasks.push_back([](int x, int y) { return x + y; });
-
- for (std::function<void(int, int)> f : tasks) {
- f(33, 66);
- }
- }
- class C {
- public:
- void memfunc(int x, int y) {}
- };
-
- int main()
- {
- std::function<void(const C&, int, int)> mf;
- mf = &C::memfunc;
- mf(C(), 42, 77);
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。