文章目录
C++之type_traits
is_floating_point …的使用
- 一般在定义打印模板函数的时候,当我们用printf进行终端日志打印,需要根据打印的类型来设置flag,所以这个时候判断数据类型就很必要了, 我们可以根据is_same,is_float_point, is_integral等来确定类型情况。可以参考cuda打印实例
std::is_floating_point<yourtype>::value;
std::is_same<type1, type2>::value;
- 这些is_xxx的函数,如何定义的; 有什么可以学习的点
std::enable_if::type的使用
- c++的原则就替换失败并非错误(SFINAE)。std::enable_if 就是满足条件时类型是有效的
- 所谓的SFINAE规则就是在编译时进行查找替换,对于重载的函数,如果能够找到合适的就会替换,如果第一个不合适并不会报错,而会使用下一个替换直到最后一个,如果都不满足要求,那么才会报错。出现二义性的话也会报错。
- 主要两个应用
- 类型判断,可以自定义类型判断
//判断类型
template <typename _Tp>
struct Smart_pointer : public false_type {};
template <typename _Tp>
struct Smart_pointer<std::weak_ptr<_Tp>> : public true_type {};
template <typename _Tp>
struct Smart_pointer<std::shared_ptr<_Tp>> : public true_type {};
template <typename _Tp>
struct is_smart_pointer : public Smart_pointer<typename std::remove_cv<_Tp>::type>{};
template <typename _Tp>
typename enable_if<is_smart_pointer<_Tp>::value,void>::type check(_Tp p){
std::cout << "is smart pointer" << std::endl;
}
template <typename _Tp>
typename enable_if<!is_smart_pointer<_Tp>::value,void>::type check(_Tp p){
std::cout << "not smart pointer" << std::endl;
}
void test_enable_if(){
int *p = new int(3);
std::shared_ptr<int> sp = std::make_shared<int>(3);
check(sp);
check(p);
delete p;
}
- 返回值指定,根据输入类型判断返回值
template <typename _Tp>
typename enable_if<std::is_integral<_Tp>::value,bool>::type is_odd(_Tp i){return i&0x1;}
void test_is_odd(){
std::cout << std::boolalpha << is_odd(10) << std::endl;
}
Apollo开源代码中的一个实例; 利用C++的SFINAE原则,实现在类的继承过程中,上层类中定义一个必执行的函数,里面调用子类可能实现可能不实现的具体函数,这个时候就用到了这个特性,通过模板推导机制,实现子类定义这个具体操作时,这个必执行的函数会调用这个具体操作,当没有定义是,就按照这个必执行函数的默认操作去执行。
#include <type_traits>
#include <utility>
// apollo: cyber/base/macros.h
#define DEFINE_TYPE_TRAIT(name, func) \
template <typename T> \
struct name { \
template <typename Class> \
static constexpr bool Test(decltype(&Class::func)*) { \
return true; \
} \
template <typename> \
static constexpr bool Test(...) { \
return false; \
} \
\
static constexpr bool value = Test<T>(nullptr); \
}; \
\
template <typename T> \
constexpr bool name<T>::value;
// apollo: cyber/common/macros.h
/**
template <typename T>
struct HasShutdown {
template <typename Class>
static constexpr bool Test(decltype(&Class::Shutdown)*) {
return true;
}
template <typename>
static constexpr bool T(...) {
return false;
}
static constexpr bool value = Test<T>(nullptr);
};
template <typename T>
constexpr bool HasShutdown<T>::value;
*/
DEFINE_TYPE_TRAIT(HasShutdown, Shutdown)
template <typename T>
typename std::enable_if<HasShutdown<T>::value>::type CallShutdown(T *instance) {
instance->Shutdown();
}
template <typename T>
typename std::enable_if<!HasShutdown<T>::value>::type CallShutdown(
T *instance) {
(void)instance; // 可以自定义任何默认的动作。
}
/** 分析
1. 当instance实例的类中有Shudown时,第一个模板中HasShutdown<T>::value是true,则enable_if<true>::type是合法的;则第一个模板函数被匹配;而此时!HasShutdown<T>::value是false,则第二个模板函数匹配有问题,所以根据C++的SFINAE原则,则第一个被推导出来;所以当调用CallShutdown时,第一个函数被调用,而其有会调用instance中的shutdown函数。
3. 反之,当instance中没有shutDown,则第一个的HasShutdown<T>::value是false,则std::enable_if<HasShutdown<T>::value>::type非法,所以第一个模板不能被推导出来,而第二个模板被推导出来,所以当调用CallShutdown时,第二个函数形式被调用;
从而这就实现了,当有Shutdown的时候调用自己定义的,否则不做任何事情。
*/
// 下面是CallShutdown被使用时的场景, 通过在一个单例中cleanup里调用,来实现用户定制或不定制的情况。
#define DECLARE_SINGLETON(classname) \
public: \
static classname *Instance(bool create_if_needed = true) { \
static classname *instance = nullptr; \
if (!instance && create_if_needed) { \
static std::once_flag flag; \
std::call_once(flag, \
[&] { instance = new (std::nothrow) classname(); }); \
} \
return instance; \
} \
\
static void CleanUp() { \
auto instance = Instance(false); \
if (instance != nullptr) { \
CallShutdown(instance); \
} \
} \
\
private: \
classname(); \
DISALLOW_COPY_AND_ASSIGN(classname)
std::remove_cv
- 去掉变量的const, volatile属性,获取其原始类型信息。
实现方式
- 模版推导丢弃const和volatile参数。
性能开销
- 模版推导完成操作,不涉及运行时开销。
调用stl
- remove_const
- remove_volatile
作者:i_need_job
链接:https://www.jianshu.com/p/a771299d3a89
来源:简书