6

下面的代码演示了我无法解释的 gcc 4.6.2 的行为。第一个函数声明了一个 vec_t 类型的静态数组,其中 vec_t 是 unsigned char 的 typedef 别名。第二个函数是相同的,只是 vect_t 的类型是一个模板参数。第二个函数无法编译并诊断为“错误:'bitVec' 的存储大小不是恒定的”。

#include <limits>

void bitvec_func()
{
    const std::size_t       nbits = 1e7;
    typedef unsigned char   vec_t;
    const std::size_t       WLEN  = std::numeric_limits<vec_t>::digits;
    const std::size_t       VSIZ  = nbits/WLEN+1;
    static vec_t            bitVec[nbits/VSIZ];    // Compiles fine
}

template <typename T>
void bitvec_func()
{
    const std::size_t       nbits = 1e7;
    typedef T               vec_t;
    const std::size_t       WLEN  = std::numeric_limits<vec_t>::digits;
    const std::size_t       VSIZ  = nbits/WLEN+1;
    static vec_t            bitVec[nbits/VSIZ];    // "error: storage size of ‘bitVec’ isn’t constant"
}

void flarf()
{
    bitvec_func();
    bitvec_func<unsigned char>();
}

在我看来,使用参数 <unsigned char> 实例化模板应该会导致编译器生成与第一个函数相同的代码。任何人都可以提供任何见解,为什么这似乎不是这种情况?

[附录:第二个函数使用“-std=c++0x”或“-std=gnu++0x”编译,但我仍然想了解在早期的语言定义下它是如何/是否是错误的。]

ETA:
如果 nbits 的初始值设定项更改,第二个函数将编译:

const std::size_t       nbits = 1e7;              // Error
const std::size_t       nbits = (std::size_t)1e7; // Okay
const std::size_t       nbits = 10000000.0;       // Error
const std::size_t       nbits = 10000000;         // Okay

换句话说,似乎 ifnbits用整数类型的表达式初始化,然后nbits在 的定义中被视为常量bitVec。如果nbits改为使用浮点表达式初始化,编译器不再将其视为 的 维度表达式中的常量bitVec,并且编译失败。

我在 C++ 中调用“编译器错误”比在 C 中要舒服得多,但我想不出任何其他原因使上述 4 种情况在语义上不完全相同。还有人愿意发表意见吗?

4

2 回答 2

6

在 gcc 4.7.0 上编译您的代码后-ansi,我能够重现此警告:

warning: ISO C++ forbids variable length array 'bitVec' [-Wvla]

此警告出现在两个 bitVec,而不仅仅是模板函数中的一个。然后我意识到该行nbits = 1e7;正在将 a 分配doubleunsigned int. 我认为正因为如此,由于某种原因导致nbits不是一个常量表达式。您的代码为非模板化版本编译的原因是因为 gcc 的可变长度数组扩展。此外,由于某种原因,您的 gcc 版本不允许在函数模板中使用可变长度数组。将您的代码更改1e7;10000000.

编辑

我问了另一个关于规则的问题。答案是在 C++03 中代码是无效的,但在 C++11 中是可以的。

于 2012-06-07T00:34:54.993 回答
0

答案肯定是在产生错误的编译阶段,数组索引的存储大小不是恒定的——即它在模板扩展的上游。动态数组不是 C++ 98/03 的一部分,它们是 gcc 扩展(最初是 C)。所以这个错误实际上是正确的,即使实现看起来很奇怪。据推测,GCC 为模板化数组达到了标准合规性路径,在需要时支持它们,否则会抛出错误,但静态类型的数组会命中“C”路径,从而自动获取 gcc 扩展。

于 2012-06-06T23:32:00.880 回答