C++之type_traits与SFINAE用法

文章目录

#include <assert.h>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

using namespace std;

// 一定要加constexpr,这样在T为int时,return t.substr(1);才不会报错
template <typename T> auto func(T t) {
    
    
  if constexpr (std::is_same_v<T, int>)
    return t + 1;
  else
    return t.substr(1);
}

// decltype与std::decay_t需要一起配合使用,否则
// func2(v);中,这里decltype(t[0])类型:int&
template <typename T> auto func2(T t) {
    
    
  if constexpr (std::is_same_v<std::decay_t<decltype(t[0])>, int>)
    return t[0] + 1;
  else
    return t[0].substr(1);
}

//更加正经的方式:
// 需要加typename条件:直接或者间接使用了模板的参数T
// 不需要加typename:using X = std::decay<int>::type;
// 直接依赖:if constexpr (std::is_same_v<typename T::value_type, int>)
// 间接依赖:if constexpr (std::is_same_v<std::decay_t<decltype(t[0])>, int>)
template <typename T> auto func3(T t) {
    
    
  using X = std::decay<int>::type;
  if constexpr (std::is_same_v<typename T::value_type, int>)
    return t[0] + 1;
  else
    return t[0].substr(1);
}
// 判断函数返回值是否是void,方法1:区别小用if constexpr
template <typename F> auto invoke(F f) {
    
    
  // 判断函数返回值是否是void
  // 等价于if constexpr (std::is_void_v<decltype(f())>)
  // 等价于:if constexpr
  // (std::is_void_v<std::invoke_result_t<F>>),表示函数参数为空时,函数调用的返回值,没有参数变量f的时候可以使用
  // 若F没有构造函数,if constexpr
  // (std::is_same_v<decltype(std::declval<F>()()), void>)
  //进一步判断函数是否是const修饰的,std::declval<F const&>()()
  if constexpr (std::is_same_v<decltype(f()), void>) {
    
    
    f();
    printf("leave....\n");
  } else {
    
    
    auto ret = f();
    printf("leave....\n");
    return ret;
  }
}

// 判断函数返回值是否是void,方法2,区别大
// C++20做法:g++ test16.cc -std=c++20
// template <typename F>
// requires(std::is_void_v<std::invoke_result_t<F>>) auto invoke2(F f) { f(); }

// template <typename F>
// requires(!std::is_void_v<std::invoke_result_t<F>>) auto invoke2(F f) {
    
    
//   auto ret = f();
//   return ret;
// }

// 没有requires,C++SFINAE做法:
// 等价于:EQUIRES(std::is_void_v<decltype(std::declval<F>()())>)
#define REQUIRES(x) std::enable_if_t<(x), int> = 0
template <typename F, REQUIRES(std::is_void_v<std::invoke_result_t<F>>)>
auto invoke3(F f) {
    
    
  f();
}

// 等价于:EQUIRES(!std::is_void_v<decltype(std::declval<F>()())>),因为形参f在这里是没有的,所以只能由编译器构造一个F(),注意lambda的构造函数是不存在的,这里反而能构造一个lambda对象
template <typename F, REQUIRES(!std::is_void_v<std::invoke_result_t<F>>)>
auto invoke3(F f) {
    
    
  auto ret = f();
  return ret;
}

template <typename F, REQUIRES(std::is_void_v<decltype(std::declval<F &>()())>)>
auto invoke4(F f) {
    
    
  f();
}
template <typename F,
          REQUIRES(!std::is_void_v<decltype(std::declval<F &>()())>)>
auto invoke4(F f) {
    
    
  auto ret = f();
  return ret;
}

///

struct myclass {
    
    
  void dismantle() {
    
     printf("rm -rf course\n"); }
};

struct myteacher {
    
    
  void rebel() {
    
     printf("rm -rf gench\n"); }

  void rebel(int i) {
    
     printf("rm -rf gench-%d\n", i); }
};

// 判断某个成员函数是否存在.C++20做法:
// g++ test16.cc -std=c++20
template <typename T> void gench(T t) {
    
    
  if constexpr (requires {
    
     t.dismantle(); }) {
    
    
    t.dismantle();
  } else if constexpr (requires {
    
     t.rebel(); }) {
    
    
    t.rebel();
  } else if constexpr (requires(int i) {
    
     t.rebel(i); }) {
    
    
    for (int i = 1; i <= 2; ++i) {
    
    
      t.rebel(i);
    }

  } else if constexpr (requires {
    
     t.rebel(std::declval<int>()); }) {
    
    
    t.rebel(0);
  } else {
    
    
    printf("======\n");
  }
}
/
// 笨蛋方法枚举,每次增加一个类都要写一个全特化,蛋疼
template <typename T> struct enum_has_dismantle {
    
    
  static constexpr bool value = false;
};

template <> struct enum_has_dismantle<myclass> {
    
    
  static constexpr bool value = true;
};

template <> struct enum_has_dismantle<myteacher> {
    
    
  static constexpr bool value = false;
};

template <typename T> void gench2(T t) {
    
    
  if constexpr (enum_has_dismantle<T>::value) {
    
    
    t.dismantle();
  } else {
    
    
    printf("======\n");
  }
}

/

// 判断某个成员函数是否存在.C++14做法SIFANE:
template <class T, class = void> struct has_dismantle {
    
    
  static constexpr bool value = false;
};

// std::void_t<>只是关心<>里面的内容是否正确
template <class T>
struct has_dismantle<T, std::void_t<decltype(std::declval<T>().dismantle())>> {
    
    
  static constexpr bool value = true;
};

template <class T, class = void> struct has_rebel {
    
    
  static constexpr bool value = false;
};

template <class T>
struct has_rebel<
    T, std::void_t<decltype(std::declval<T>().rebel(std::declval<int>()))>> {
    
    
  static constexpr bool value = true;
};

template <class T, class = void> struct has_nopara_rebel {
    
    
  static constexpr bool value = false;
};
template <class T>
struct has_nopara_rebel<T, std::void_t<decltype(std::declval<T>().rebel())>> {
    
    
  static constexpr bool value = true;
};

template <typename T> void gench3(T t) {
    
    
  if constexpr (has_dismantle<T>::value) {
    
    
    t.dismantle();
  } else if constexpr (has_rebel<T>::value) {
    
    
    for (int i = 1; i <= 2; ++i) {
    
    
      t.rebel(i);
    }

  } else {
    
    
    printf("======\n");
  }
}

// 不使用C++17的constexpr
template <class T, REQUIRES(has_dismantle<T>::value)> void gench4(T t) {
    
    
  t.dismantle();
}

template <class T, REQUIRES(!has_dismantle<T>::value && has_rebel<T>::value)>
void gench4(T t) {
    
    
  for (int i = 1; i <= 4; i++) {
    
    
    t.rebel(i);
  }
}

template <class T, REQUIRES(!has_dismantle<T>::value && !has_rebel<T>::value)>
void gench4(T t) {
    
    
  printf("no any method supported!\n");
}

int main(int argc, char **argv) {
    
    
  int i = 0;
  string s = "hello";
  func(i);
  func(s);

  vector<int> v = {
    
    1, 2, 3};
  v.operator[](1);
  func2(v);

  func3(v);

  std::cout << __cplusplus << std::endl;

  invoke([]() -> int {
    
     return 1; });
  invoke([]() -> void {
    
     return; });

  static_assert(std::is_void_v<void>);
  static_assert(std::is_void_v<const void>);
  static_assert(std::is_void_v<const volatile void>);

  invoke4([&]() mutable -> int {
    
     return 1; });

  myclass ms;
  myteacher mt;
  gench(ms);
  gench(mt);

  gench3(ms);
  gench3(mt);

  return 0;
}

参考:

猜你喜欢

转载自blog.csdn.net/u011436427/article/details/131387907