Michael Doubez said:
template< class T >
struct Foo {
static int const N = T::size;
static int values[N];};
template< class T > int const Foo<T>::N;
template< class T > int Foo<T>::values[Foo<T>::N] = {};
struct Bar {
enum { size = 42 } ;
};
int main() {
Foo<Bar> f;
return f.N;
}
Which yield under gcc (4.1.3 and 4.3.3):
error: conflicting declaration 'int Foo<T>::values [Foo::N]
error: 'Foo<T>::values' has a previous declaration as 'int
Foo<T>::values [Foo<T>::N]'
error: declaration of 'int Foo<T>::values [Foo<T>::N]' outside of
class is not definition
The compiler seems confused by the size because it accepts the code
- if I replace the definition of N by:
enum { N = T::size };
- if I don't use N but T::size
This code pass under Comeau online (not in codepad).
Is this a compiler bug or is it expected behavior ?
Well, under gcc 4.4.5 it compiles fine...
Ok. I suppose this is a bug and it has since been fixed.
Have you tried to remove the declaration/initialization pair:
template< class T > int const Foo<T>::N;
template< class T > int Foo<T>::values[Foo<T>::N] = {};
Since they really aren't necessary, trying to remove them would be my
first try.
They are necessary.
Otherwise you get UB and if used, you are likely to get link errors.
--N is dependant on the template parameter.
because:
static int const N = T::size. /*This does not instanciate the template.*/
--This declare a static member of integral type taking its value from
--the template parameter.
Yes you are correct but the next occurence of N is not a template:
static int values[N];
I meant this cannot be resolved until the template is instanciated. But this
is all done by the compiler anyway. I was thinking about templates to much
and missed the obvious non-template issue.
template< class T >
struct Foo{
static int const N= T::size;
//static int values[N];
int arr[N];
};
The above would need exlicit instaciation if I was correct but it doesn't
The compiler cannot know what T::size refers to without a template
instanciation, so you need:
template< class T > int Foo<T>::values[Foo<T>::N]; /*Explicitly instaciate
the template*/
--It defines the static member. Nothing is instantiated.
Yes it defines the static.
--The only work done by the compiler at this point is syntax checking.
I disagree with that, I think the template must be instanciated, by the
compiler, to define N. But I'm not certain , I may be wrong.
I think there is more instanciations created under the hood than meets the
eye.
Note: afaik the ( ={} ) doesn't do anything and is not required.
--It initialise the values of the array to the default value (0 in this
--case).
--You are right, it is not truly needed since global variable are 0
--initialized by default; I use it out of habit.
No I was wrong , I though the expression was an explicit template
instanciation and it isn't.
You are right this line defines the static array.
ok now it knows that N is T::size:
static int values[N]; /* --- N is T::size. ---*/
--That is the buggy step, the compiler doesn't recognize the definition
--relatively to the declaration (irrelevantly of any instantiation).
Yes I see how this can be a complex step for the compiler. I'm not sure how
a compiler implements this because it cannot know the value of N until is
has created a template instanciation, I think.
AFAICT you do not need:
template< class T > int const Foo<T>::N; /* This is instanciated
implicitly
with Foo<Bar> f anyway*/
--No it is not implicity instanciated without a definition.
--It is true that I don't really need it, provided I don't take a
--reference to it: the compiler directly reuse the value specified at
--declaration time; but it is formally UB.
How would taking a reference make any difference?
Does the template make any differenece to if you just did:
struct Foo{
static int const N = 42;
};