9

The following code looks fine to me:

    #include <stdio.h>

    template <typename T>
    struct A
    {
        static float m_kA[];
    };

    template <typename T>
    float A<T>::m_kA[] = {1.0f, 2.0f, 3.0f};

    int main()
    {
        printf("%d\n", 
            sizeof(A<unsigned int>::m_kA) /
            sizeof(A<unsigned int>::m_kA[0]));
        return 0;
    }

But when i compile with VC9 i get the following error

error C2070: 'float []': illegal sizeof operand

I would expect this code to compile. Am i missing something? Does anyone know a way to fix this strange behavior (note that the exact same thing without the template compiles fine and outputs 3).

Note that removing the template is not an option, i made this example to reproduce a problem that i'm having in a code where i need the type containing the array to be a template.

Thanks

valerio
  • 271
  • 3
  • 9
  • 2
    FWIW, Compiles fine on GCC 4.7.1: http://liveworkspace.org/code/19f48dbdb07463b08a310c168ab59a67. Watch it be yet another MSVC bug or something. – chris Sep 18 '12 at 21:07
  • I don't think the expression is calculating what you think it is calculating. – Nobody moving away from SE Sep 18 '12 at 21:08
  • 4
    @Nobody `sizeof array / sizeof array[0]` is a common idiom that calculates the length of the array. What do you think it calculates, or what do you think other people think it calculates? –  Sep 18 '12 at 21:09
  • @Nobody, Why do you say that? Arrays don't decay when you `sizeof` them. – chris Sep 18 '12 at 21:09
  • 1
    As for the actual problem, is it an option (even if it is troublesome) to explicitly specify the length of the array in the in-class declaration? –  Sep 18 '12 at 21:10
  • Yes i tried GCC, i posted hoping that someone knows how to avoid this on Microsoft VC9. I would expect it to give me the number of elements in m_kA, but it's not the problem. I would be happy to just have sizeof(A::m_kA) compile. Unluckily i can't specify the length before, the list is generated via variadic macros that i would like not to calculate the length of – valerio Sep 18 '12 at 21:11
  • @hvd: I thought this would only work for arrays with given length? I somehow overlooked the definition though. – Nobody moving away from SE Sep 18 '12 at 21:42
  • @Nobody Yes, that's right, but it will never calculate the wrong thing as long as you're dealing with arrays: it will either give the correct result (if the length is known), or give a hard compiler error (if the length is unknown). –  Sep 18 '12 at 21:59
  • What if you lift the static member inside a non-template class, and the template inherits from that? I cannot test this solution myself. – Luc Danton Sep 18 '12 at 22:15
  • @LucDanton That's not an option, in the actual code i'm using T in the initializer so i need the static to be a template. – valerio Sep 19 '12 at 06:16

2 Answers2

7

It's well defined. Do note that in the class definition, m_kA is declared with type float[], which is an incomplete type and cannot be used in tandem with sizeof. In the definition of m_kA, it is redeclared to have type float[3], after which it is okay to use sizeof. (8.3.4 governs the meaning of array declarations.)

From 3.4.6 Using-directives and namespace aliases [basic.lookup.udir]:

10 After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.

From 3.9.2 Compound types [basic.compound]:

6 [...] The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types. [...]

A workaround for your compiler issues would be to declare m_kA with a complete type outright. Another static member holding the size could be helpful, too.

[ I'm quoting from C++11 but to the best of my knowledge C++03 followed the same rules. ]

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • 1
    I would be very happy to inline the initializer, if C++ allowed me to do it. The problem is that as far as i know i can only do that only for local or global variables, and as such i'm not able to make a template for it. Also as i said before i find it difficult to justify VC9 in this, because if i only remove the template everything works. – valerio Sep 18 '12 at 21:27
  • 1
    Thinking some more, constructs such as [this](http://pastebin.com/tumC3NBU) (note: not valid C++) are a legitimate reason why the type *should* perhaps remain `float[]` rather than `float[3]` with templates. But as we've seen in the now deleted discussion, the type really is `float[3]`. –  Sep 18 '12 at 22:45
6

http://ideone.com/3ssVi

it compiles fine with G++.

As far as i can see it can be related to this bug:

http://connect.microsoft.com/VisualStudio/feedback/details/759407/can-not-get-size-of-static-array-defined-in-class-template

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
CyberGuy
  • 2,783
  • 1
  • 21
  • 31