编译错误? g ++允许可变大小的静态数组,除非函数是模板化的

时间:2012-06-06 23:14:54

标签: c++ templates gcc dynamic-arrays

以下代码演示了我无法解释的gcc 4.6.2的行为。第一个函数声明一个类型为vec_t的静态数组,其中vec_t是unsigned char的typedef'd别名。除了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>();
}

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

[附录:第二个函数编译为“-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

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

我在C ++中调用“编译器错误”比在C中调用错误要差得多,但我想不出上述4种情况在语义上不相同的任何其他原因。还有其他人关心吗?

2 个答案:

答案 0 :(得分:6)

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

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

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

修改

我问another question有关规则。答案是在C ++ 03中代码无效,但在C ++ 11中它是可以的。

答案 1 :(得分:0)

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