【问题标题】:Deduce non-type template parameter推导非类型模板参数
【发布时间】:2012-01-12 14:57:36
【问题描述】:

是否可以从模板函数参数中推导出非类型模板参数?

考虑这个简单的模板:

template <int N> constexpr int factorial()
{
        return N * factorial<N - 1>();
}

template <> constexpr int factorial<0>()
{
        return 1;
}

template <> constexpr int factorial<1>()
{
        return 1;
}

我希望能够更改 factorial 以便我可以这样称呼它:

factorial(5);

并让编译器在编译时计算出 N 的值。 这可能吗?也许添加了一些花哨的 C++11?

【问题讨论】:

  • 您为什么要这样做? factorial 函数不接受任何参数。 factorial(5) 比正确的 factorial&lt;5&gt;() 有什么好处?
  • @CodyGray:我认为这个想法是创建一个通用的factorial 函数,如果它给出了一个编译时已知的表达式,则能够在编译时计算结果,但也能够计算如果输入是普通变量,则在运行时。
  • @Matteo:据我所知,由于表达式被声明为constexpr,因此在运行时没有计算任何内容。无论如何,我不明白为什么使用标准语法传递“普通变量”而不是 5 会有任何问题。这里的问题是“我怎样才能避免输入尖括号”,我不明白动机。
  • 我想避免更改大量代码。另外,我发现它更惯用,但这是我可以轻松传递的东西;)
  • @CodyGray: constexpr 就像@Matteo 所说的那样工作:如果constexpr 函数调用中的所有参数都是编译时常量,则调用将在编译时进行评估,否则,它会被执行在运行时像任何其他函数一样。

标签: c++ c++11 templates template-argument-deduction


【解决方案1】:

我相信您当前的代码通常如下编写:

constexpr factorial (int n)
{
    return n > 0 ? n * factorial( n - 1 ) : 1;
}

如果你用一个常量表达式来调用它,比如factorial(5),那么所有的编译器魔法都会发挥作用。但是如果你这样做int a = 3; factorial(a),那么我认为它会依赖于一个传统的函数——即它不会建立一个预先计算的答案的查找表。

一般来说,如果可以的话,你应该将每个函数和构造函数标记为constexpr。你什么都不会丢失,编译器会在必要时将其视为普通函数。

【讨论】:

  • 我不知道 constexpr 像这样选择退出。可悲的是,代码只是一个示例,我需要在实际函数中使用一个固定大小的数组,所以我需要一个以数组大小作为参数的模板。
  • 也许在编译时构建一个指向模板化函数的函数指针数组,并在运行时查找它们? factorial[3](.. other args ..)
  • @pezcode:将阶乘更改为调用模板代码的宏,Aaron:你最好构建一个结果数组
  • @MooingDuck,是的,但请参阅 pezcode 对此答案的评论 - 我认为真正的应用程序比这更复杂。我的印象是一些 args 是这些模板化整数,而它们也可能是其他参数。
  • 如果您可以将每个函数和构造函数标记为 constexpr 而不会产生任何负面影响,那么它(再次)提出了一个问题,即为什么这不是 C++11 编译器的默认值...我猜猜这又是神圣的向后兼容问题。
【解决方案2】:

不可能,除非你有时光机。

函数的参数在运行时处理。是的,在 your 的情况下,它是一个字面常量,但这是一种特殊情况。

在函数定义中,参数types在编译时是固定的(因此可以用来推导模板参数),但参数values只固定在运行时。

为什么需要这个?是为了不用输入&lt;&gt;'s 吗?

【讨论】:

  • 差不多,我有一个庞大的代码库,其中调用了几个我重写为模板的函数。替换名称是微不足道的,但将所有 f(2, 3, "abc", true) 转换为 f&lt;2, 3, true&gt;("abc") 有点棘手。
  • 用宏将f(2, 3, "abc", true) 转换成f_impl&lt;2, 3, true&gt;("abc") 怎么样?
【解决方案3】:

我认为你做不到;唯一可以做到这一点的方法是使用 constexpr 函数参数,然后将其作为 template 参数传递给 factorial 的模板版本,但不允许使用 constexpr 函数参数。

【讨论】:

    【解决方案4】:

    不,你不能那样做。模板参数只能从函数参数的类型推导出来,不能从推导出来,一般在编译时是不知道的。

    当然,您可以将factorial 重写为非模板constexpr 函数;如果参数是已知的,那么它将在编译时进行评估。

    【讨论】:

      【解决方案5】:

      不,这是不可能的,除非你想创建一个巨大的 switch 语句:

      int getFactorial( const int v )
      {
        switch ( v )
        {
          case 1 : return factorial<1>();
          case 2 : return factorial<2>();
          //etc
          default:
             ;
        }
        return 0;
      }
      

      【讨论】:

        【解决方案6】:

        使用邪恶的宏:

        #define factorial(X) factorial<X>()
        

        【讨论】:

          【解决方案7】:

          此类问题的一种可能的解决方法是使用一些struct,如下所示:

          template <int X>
          struct constant {
              operator int () { return X; }
          };
          

          然后我们可以像这样在constant&lt;X&gt; 上重载:

          template <int X>
          void f (constant<X>)
          

          在编译时和运行时重载 f (int) 时使用 X 的值。烦人的部分是它需要在调用站点上进行一些纪律,因为每个文字常量都必须包装为constant&lt;X&gt; {}(使用宏或类似的东西)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-07
            • 2018-05-01
            • 1970-01-01
            • 1970-01-01
            • 2020-07-09
            • 1970-01-01
            • 1970-01-01
            • 2022-12-22
            相关资源
            最近更新 更多