元编程 - 类幂函数

Metaprogramming - power-like function

我想定义类似于幂函数的模板a^n

  1. a^n = -1 其中 a < 0n < 0
  2. a^0 = 0(所以不完全是std::pow
  3. 否则std::pow

我在定义第 1 点的条件时遇到问题 - 我假设这将是 enable_if 和一些定义的 constexpr 检查整数是否为负数的组合。

我为第 1. 点写的内容(在下面注释掉)可能没有意义,因为它无法编译。我只是从元编程开始,老实说我不太明白。如果您能提供解释,我将不胜感激 and/or 一些您在进入主题时发现有帮助的资源。

#include <iostream>
#include <cmath>

// std::pow
template <int a, int n>
struct hc {
  enum { v = a * hc<a, n - 1>::v };
};
// to break recursion from getting to a^0=0 
template <int a>
struct hc<a, 1> {
  enum { v = a };
};

// a^0 = 0
template <int a>
struct hc<a, 0> {
  enum { v = 0 };
};

// a^n=-1 for negative a or n
/* 
template <int i>
constexpr bool is_negative = i < 0;

// a ^ n = -1, where a < 0 or n < 0
template <int a, int n,
          typename std::enable_if<is_negative<a> || is_negative<n>>::type>
struct hc {
  enum { v = -1 };
};
*/

int main() {
  // a^0=0
  std::cout << hc<0, 0>::v << " -> 0^0=0\n";
  std::cout << hc<3, 0>::v << " -> 3^0=0\n";

  // a^n=std::pow
  std::cout << hc<1, 1>::v << " -> 1^1=" << std::pow(1, 1) << '\n';
  std::cout << hc<2, 2>::v << " -> 2^2=" << std::pow(2, 2) << '\n';
  std::cout << hc<0, 2>::v << " -> 0^2=" << std::pow(0, 2) << '\n';
  std::cout << hc<3, 2>::v << " -> 3^2=" << std::pow(3, 2) << '\n';
  std::cout << hc<3, 7>::v << " -> 3^7=" << std::pow(3, 7) << '\n';

  // a^n=-1 for negative a or n
  std::cout << hc<-3, 7>::v << " -> -3^7=-1\n";
  std::cout << hc<3, -7>::v << " -> 3^-7=-1\n";
  std::cout << hc<0, -7>::v << " -> 0^7=-1\n";
  std::cout << hc<-3, 0>::v << " -> -3^0=-1\n";
}


有几种方法

更简单的 IMO,将是 constexpr 函数

constexpr int hc_impl(int a, int n)
{
    if (a < 0 || n < 0) return -1;
    if (n == 0) return 0;
    int res = 1;
    for (int i = 0; i != n; ++n) {
       res *= a;
    }
    return res;
};

template <int a, int n>
struct hc
{
    constexpr int v = hc_impl(a, n);
};

使用结构的旧方法,您可以为调度添加一个额外的参数,例如:

template <int a, int n, bool b = (a < 0 || n < 0)>
struct hc;

template <int a, int n>
struct hc<a, n, true> {
  enum { v = -1 };
};
template <int a>
struct hc<a, 1, true> {
  enum { v = -1 };
};
template <int a>
struct hc<a, 0, true> {
  enum { v = -1 };
};


template <int a, int n>
struct hc<a, n, false> {
  enum { v = a * hc<a, n - 1>::v };
};
// to break recursion from getting to a^0=0 
template <int a>
struct hc<a, 1, false> {
  enum { v = a };
};

// a^0 = 0
template <int a>
struct hc<a, 0, false> {
  enum { v = 0 };
};

这就是我使用模板 constexpr 的方式:

template<int a, int n>
constexpr int pow()
{
    if ((a < 0) || (n < 0)) return -1;
    if (n == 0) return 0;

    int result = 1;
    for (int i = 0; i < n; i++) result *= a;
    return result;
}

int main()
{
    static_assert(pow<0,0>() == 0);
    static_assert(pow<2, 0>() == 0);
    static_assert(pow<-1, 0>() == -1);
    static_assert(pow<1, -1>() == -1);
    static_assert(pow<2, 3>() == 8);
}

最后一种情况的部分专业化语法不正确:template<.....> struct hn 之后 <> 中应该有一些东西。

所以,像这样的东西 几乎 工作:

// a ^ n = -1, where a < 0 or n < 0
template <int a, int n,
          typename std::enable_if<is_negative<a> || is_negative<n>>::type>
struct hc<a, n> {
  enum { v = -1 };
};

enable_if::type 是一种类型,您必须将其放在可以 SFINAE 的位置,而不仅仅是 template<> 中的某个位置。您通常将其放在函数签名中或部分模板特化中。

像这样:

// You have to change your general case definition.
// std::pow
template<int a, int n, typename /*DummyUnusedType*/ = void>
struct hc {
  enum { v = a * hc<a, n - 1>::v };
};

// ... your existing definitions here ...

// a ^ n = -1, where a < 0 or n < 0
template <int a, int n>
struct hc<a, n, typename std::enable_if<is_negative<a> || is_negative<n>>::type> {
  enum { v = -1 };
};

你实际上甚至不需要 is_negativetypename:

struct hc<a, n, std::enable_if_t<(a < 0 || n < 0)>> {  // Parens are optional

唯一剩下的问题是您的 <a, 0> 案例与这个案例相交负 as。您可以使用相同的技巧将其限制为非负数 a

一般来说,constexpr 功能更优越,正如其他答案所建议的那样。