元编程 - 类幂函数
Metaprogramming - power-like function
我想定义类似于幂函数的模板a^n
a^n = -1
其中 a < 0
或 n < 0
a^0 = 0
(所以不完全是std::pow
)
- 否则
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_negative
和 typename
:
struct hc<a, n, std::enable_if_t<(a < 0 || n < 0)>> { // Parens are optional
唯一剩下的问题是您的 <a, 0>
案例与这个案例相交负 a
s。您可以使用相同的技巧将其限制为非负数 a
。
一般来说,constexpr
功能更优越,正如其他答案所建议的那样。
我想定义类似于幂函数的模板a^n
a^n = -1
其中a < 0
或n < 0
a^0 = 0
(所以不完全是std::pow
)- 否则
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_negative
和 typename
:
struct hc<a, n, std::enable_if_t<(a < 0 || n < 0)>> { // Parens are optional
唯一剩下的问题是您的 <a, 0>
案例与这个案例相交负 a
s。您可以使用相同的技巧将其限制为非负数 a
。
一般来说,constexpr
功能更优越,正如其他答案所建议的那样。