Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler bug? g++ allows variable-size static arrays, unless function is templated

The code below demonstrates a behavior of gcc 4.6.2 that I can't account for. The first function declares a static array of type vec_t, where vec_t is a typedef'd alias for unsigned char. The second function is identical, except that the type of vect_t is a template parameter. The second function fails to compile with diagnostic "error: storage size of ‘bitVec’ isn’t constant".

#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>();
}

It seems to me that instantiating the template with argument <unsigned char> should cause the compiler to generate the same code as the first function. Can anyone offer any insight into why this does not seem to be the case?

[Addendum: the second function will compile with "-std=c++0x" or "-std=gnu++0x", but I'd still like to understand how/if it's wrong under the earlier language definitions.]

ETA:
The second function will compile if the initializer for nbits is changed:

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

In other words, it seems that if nbits is initialized with an expression of an integral type, then nbits is treated as a constant in the definition of bitVec. If nbits is instead initialized with a floating-point expression, the compiler no longer sees it as constant in the expression for the dimension of bitVec, and the compilation fails.

I'm a lot less comfortable calling "compiler bug" in C++ than I would be in C, but I can't think of any other reason that the above 4 cases would not be semantically identical. Anyone else care to opine?

like image 380
John Auld Avatar asked Jun 06 '12 23:06

John Auld


1 Answers

After compiling your code with -ansi on gcc 4.7.0, I was able to reproduce this warning:

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

This warning appeared for both bitVec, not just the one in the template function. I then realized that the line nbits = 1e7; is assigning a double to an unsigned int. I think because of this, for some reason causes nbits to not be a constant expression. The reason your code is compiling for the non-templated version is because of the variable length array extension for gcc. Also, your version of gcc for some reason doesn't allow variable length arrays in function templates. To fix your code change 1e7; to 10000000.

EDIT

I asked another question concerning the rule. The answer is in C++03 the code is invalid, but in C++11 it is okay.

like image 94
Jesse Good Avatar answered Sep 18 '22 03:09

Jesse Good