【问题标题】:Metaprogramming - power-like function元编程 - 类似权力的功能
【发布时间】:2022-01-02 02:47:35
【问题描述】:

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

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

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

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

#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";
}


【问题讨论】:

  • 你能接触到 C++20 和概念吗?
  • 我使用的是 C++14

标签: c++ templates metaprogramming


【解决方案1】:

有几种方法

更简单的 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 };
};

【讨论】:

  • 哇,看起来你们俩都同意 :D 太简单了!但是,如果您能告诉我如何让我的方式发挥作用,我将不胜感激——当然,如果可能的话。
【解决方案2】:

这就是我使用模板 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);
}

【讨论】:

  • @Jarod42 谢谢,已修复
【解决方案3】:

您对最后一种情况的偏特化语法不正确:您应该在 &lt;&gt; 之后在 template&lt;.....&gt; 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&lt;&gt; 内部的某个位置。您通常将其放在函数签名中或部分模板特化中。

像这样:

// 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

唯一剩下的问题是您的&lt;a, 0&gt; 案例与此案例相交以得出否定的as。您可以使用相同的技巧将其限制为非负 as。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 1970-01-01
    相关资源
    最近更新 更多