08 编译期编程

2021-05-26  本文已影响0人  奇点创客

模板元编程

template<unsigned p, unsigned d> // p是要检测的数,d是现在的除数
struct DoIsPrime { // 用d到2依次去除p,对所有结果求与
  static constexpr bool value = (p % d != 0) && DoIsPrime<p, d-1>::value; 
}; 

template<unsigned p> // 除数减到2为止
struct DoIsPrime<p, 2> {
  static constexpr bool value = (p % 2 != 0);
}; 

template<unsigned p>
struct IsPrime { // 除数从p/2开始
  static constexpr bool value = DoIsPrime<p, p / 2>::value;
}; 

// 对于几个特殊用例要单独特化
template<>
struct IsPrime<0> { static constexpr bool value = false; }; 
template<>
struct IsPrime<1> { static constexpr bool value = false; }; 
template<>
struct IsPrime<2> { static constexpr bool value = true; }; 
template<>
struct IsPrime<3> { static constexpr bool value = true; }; 
IsPrime<9>::value
// 扩展为
DoIsPrime<9, 4>::value
// 扩展为
9 % 4 != 0 && DoIsPrime<9, 3>::value
// 扩展为
9 % 4 != 0 && 9 % 3 != 0 && DoIsPrime<9, 2>::value
// 扩展为
9 % 4!=0 && 9 % 3 != 0 && 9 % 2 != 0
// 因为9%3为0,结果为false

constexpr函数

constexpr bool doIsPrime (unsigned p, unsigned d)
{
  return d != 2 ? (p % d != 0) && doIsPrime(p, d - 1) : (p % 2 != 0);
}

constexpr bool isPrime (unsigned p)
{
  return p < 4 ? !(p < 2) : doIsPrime(p, p / 2);  // 处理特殊用例
}
constexpr bool isPrime (unsigned p)
{
  for (unsigned d = 2; d <= p/2; ++d)
  {
    if (p % d == 0) return false; 
  }
  return p > 1;
}
constexpr bool b1 = isPrime(9); // 编译期计算
const bool b2 = isPrime(9); // 同样在编译期计算
// 在块作用域中编译器会决定在编译期还是运行期计算
bool fiftySevenIsPrime()
{
  return isPrime(57); // 在编译期或运行期计算
}
// 下面无论x是否为质数都会在运行期计算
int x = 42;
std::cout << isPrime(x); // 运行期计算

使用偏特化选择执行路径

// 基本辅助模板
template<int SZ, bool = isPrime(SZ)>
struct Helper;

// 如果SZ不是质数的实现
template<int SZ>
struct Helper<SZ, false> {
  ...
};

// 如果SZ是质数的实现
template<int SZ>
struct Helper<SZ, true> {
  ...
};

template<typename T, std::size_t SZ>
long f(const std::array<T,SZ>& coll)
{
  Helper<SZ> h; // 实现依赖于数组大小是否为质数
  ...
}
// 基本辅助模板(如果没有特化匹配)
template<int SZ, bool = isPrime(SZ)>
struct Helper {
  ...
};

// 如果SZ是质数的实现
template<int SZ>
struct Helper<SZ, true> {
  ...
};

SFINAE(Substitution Failure Is Not An Error)

// number of elements in a raw array:
template<typename T, unsigned N>
std::size_t len(T(&)[N])
{
  return N;
}

// number of elements for a type having size_type:
template<typename T>
typename T::size_type len(const T& t)
{
  return t.size();
}
int a[10];
std::cout << len(a); // OK:只有第一个模板匹配
std::cout << len("tmp"); // OK:只有第一个模板匹配
std::vector<int> v;
std::cout << len(v); // OK:只有第二个模板匹配
int* p;
std::cout << len(p); // 错误:找不到匹配的len函数
std::allocator<int> x;
std::cout << len(x); // 错误:能找到匹配的len函数,但不能调用size()
// number of elements in a raw array:
template<typename T, unsigned N>
std::size_t len(T(&)[N])
{
  return N;
}

// number of elements for a type having size_type:
template<typename T>
typename T::size_type len(const T& t)
{
  return t.size();
}

// fallback for all other types:
std::size_t len(...) // 这个省略号是参数包
{
  return 0;
}

int a[10];
std::cout << len(a); // OK: len() for array is best match
std::cout << len("tmp"); //OK: len() for array is best match
std::vector<int> v;
std::cout << len(v); // OK: len() for a type with size_type is best match
int* p;
std::cout << len(p); // OK: only fallback len() matches
std::allocator<int> x;
std::cout << len(x); // ERROR: 2nd len() function matches best, but can't call size() for x
namespace std {
class thread {
 public:
  ...
  template<typename F, typename... Args>
  explicit thread(F&& f, Args&&... args);
  ...
};
}
Remarks: This constructor shall not participate in overload resolution if decay_t<F> is the same type as std::thread.
namespace std {
class thread {
 public:
  ...
  template<typename F, typename... Args,
    typename = std::enable_if_t<!std::is_same_v<std::decay_t<F>, thread>>>
  explicit thread(F&& f, Args&&... args);
  ...
};
}
template<typename T>
typename T::size_type len(const T& t)
{
  return t.size();
}

std::allocator<int> x;
std::cout << len(x) << '\n'; // ERROR: len() selected, but x has no size()
template<typename T>
auto len(const T& t) -> decltype((void)(t.size()), T::size_type())
{
  return t.size();
}

if constexpr

template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
  std::cout << firstArg << '\n';
  if constexpr (sizeof...(args) > 0)
  {
    print(args...);
  }
}
template<typename T>
void f(T t)
{
  if constexpr (std::is_integral_v<T>)
  {
    if (t > 0) f(t - 1); // OK
  }
  else
  {
    undeclared(t); // error if not declared and not discarded (i.e. T is not integral)
    undeclared(); // error if not declared (even if discarded)
    static_assert(false, "no integral"); // always asserts (even if discarded)
    static_assert(!std::is_integral_v<T>, "no integral"); // OK
  }
}
int main()
{
  if constexpr (std::numeric_limits<char>::is_signed)
  {
    f(42); // OK
  }
  else
  {
    undeclared(42); // error if undeclared() not declared
    static_assert(false, "unsigned"); // always asserts(even if discarded)
    static_assert(!std::numeric_limits<char>::is_signed, "char is unsigned"); // OK
  }
}
template<typename T, std::size_t SZ>
void f(const std::array<T, SZ>& coll)
{
  if constexpr (!isPrime(SZ)) ...
}
上一篇 下一篇

猜你喜欢

热点阅读