【问题标题】:Constexpr, templates and array sizeConsexpr、模板和数组大小
【发布时间】:2013-07-06 20:27:50
【问题描述】:

我想将模板参数传递给函数调用,并将返回值用作数组的大小,即

constexpr int myPow(int a, int b){
  int result = 1;
  for(int i=0;i<b;i++)
    result *= a;
  return result;
}

template <int N>
class testClass{
public:
  testClass(){}
  int array[myPow(2,N)];
};

int main(){
  testClass<3> A;
  return 0;
}

编译器错误:

~ $ g++-4.6 test.cpp -std=gnu++0x
test.cpp: In function ‘constexpr int myPow(int, int)’:
test.cpp:6:1: error: body of constexpr function ‘constexpr int myPow(int, int)’ not a return-statement
test.cpp: At global scope:
test.cpp:12:23: error: array bound is not an integer constant before ‘]’ token

知道如何解决这个问题吗?

【问题讨论】:

  • 您的函数不能超过一个 return ...;(C++14 之前),它只使用编译时工具,否则它不会返回编译时常量。
  • 发布相关代码(即myPow 中的代码和最小完整的testClass,以及实际的编译器错误。
  • 你真的前向声明了 myPow() 吗?在testClass 中使用它时是否知道它的函数原型?
  • 我添加了整个“工作”代码。
  • 好吧,这就是克里斯所说的。在 C++11 中,您的 constexpr 函数不能超过一个 return 语句。编译器错误消息已经表明这一点。

标签: c++ templates c++11


【解决方案1】:

在 C++11 中,constexpr 函数只能包含 return 语句(有关详细信息,请参阅 here),因此您的 myPow 函数不符合 constexpr(因为它包含 @ 987654325@循环)。

您可以使用此元函数在编译时计算整数幂:

template <int N, typename Type> 
constexpr Type pow(const Type& x) 
{
    return (N > 1) ? (x*pow<(N-1)*(N > 1)>(x)) 
                   : ((N < 0) ? (static_cast<Type>(1)/pow<(-N)*(N < 0)>(x)) 
                              : ((N == 1) ? (x) 
                                          : (static_cast<Type>(1))));
}

如果你想计算2^N,你可以输入:

pow<N>(2)

注意 1:此元函数非常通用,也适用于负整数和浮点类型,因此您可以键入:pow&lt;-3&gt;(3.14)

注2:在模板中乘以N&gt;1N&lt;0是为了阻止无限递归并在分支不相关时强制模板参数等于0。这可以通过模板特化来完成,但这里使用的技术允许编写单个函数。

【讨论】:

  • 哇非常好!我正在考虑使用元函数,但不知道如何使用。
  • 是的,它非常通用,我真的很喜欢它!!!如果我不忘记的话,我可能会在两天内给你一些奖励;)
  • 但是我想知道为什么pow&lt;(N-1)*(N &gt; 1)&gt;pow&lt;(-N)*(N &lt; 0)&gt;(x)中有(N&gt;1)(N&lt;0)?对我来说似乎是多余的。
  • 因为在编写元函数时,即使没有执行,编译器也会尝试实例化所有分支。因此,如果没有这种冗余,递归将不会停止。所以N&gt;1N&lt;0在这里强制模板参数等于0以避免无限递归。我会在答案中详细说明。
【解决方案2】:

在 C++11 中,constexpr 函数非常受限制,并且您的代码不符合这些限制(您不能声明变量、改变本地状态,也不能使用大多数形式的语句——包括循环) .但是,C++1y 消除了大部分限制,并且 Clang 3.3 在其 -std=c++1y 模式下接受您的原始代码示例。

如果你需要代码在 C++11 模式下工作,你可以重写它以避开constexpr 的限制:

constexpr int myPow(int a, int b) {
  return b ? a * myPow(a, b - 1) : 1;
}

【讨论】:

    【解决方案3】:

    编辑:转到 Richard Smith 的更聪明的答案。

    根据您接受的答案,没有必要使用元函数, 将您的myPow 算法实现为constexpr-qualified 函数。

    您可以默认指数参数 = 0,然后:

    constexpr int myPow(int a, int b = 0){
        return b == 0 ? 1 : myPow(a,b - 1) * a;
    }
    

    如果您不喜欢默认该参数,那么您也可以 默认情况下,constexpr 辅助中的那个参数,其中myPow 只是 调用,例如

    namespace impl {
        constexpr int myPow(int a, int b = 0){
            return b == 0 ? 1 : myPow(a,b - 1) * a;
        }
    }
    constexpr int myPow(int a, int b){
        return impl::myPow(a,b);
    }
    

    如果并且当您至少升级到 gcc 4.7.2 时,您将能够使用 -std=c++11 甚至将那个辅助隐藏在myPow 本身中,因为您将被允许 在 constexpr 函数的主体中定义类型:

    constexpr int myPow(int a, int b){
        struct _1 {
            static constexpr int _2(int a, int b = 0) {
                return b == 0 ? 1 : _2(a,b - 1) * a;
            }
        };
        return _1::_2(a,b);
    }
    

    (虽然我认为严格来说这个纬度是 C++1y 的扩展)。

    您可能想要调整文森特的高级算法,以便 它不再是N 中的元函数,但仍然是通用的 算术类型。

    【讨论】:

    • 不需要默认参数。
    • @RichardSmith 哈!你是多么正确!我会相应地切碎它。
    • @RichardSmith。 ...不,我不会砍它,因为我认为现在会复制您更好的答案。
    猜你喜欢
    • 2013-04-04
    • 1970-01-01
    • 2018-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多