[C++] traits机制

发布于:2025-06-23 ⋅ 阅读:(16) ⋅ 点赞:(0)

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原则,实现在类的继承过程中,上层类中定义一个必执行的函数,里面调用子类可能实现可能不实现的具体函数,这个时候就用到了这个特性,通过模板推导机制,实现子类定义这个具体操作时,这个必执行的函数会调用这个具体操作,当没有定义是,就按照这个必执行函数的默认操作去执行。

  • Apollo 开源代码示例分析-HasShutdown

#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
来源:简书

如何自定义traits


网站公告

今日签到

点亮在社区的每一天
去签到