conflicting declaration in template static member

M

Michael Doubez

Hello,

I have the following minimal code:

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 ?
 
P

puppi

Hello,

I have the following minimal code:

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...
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.
 
P

Paul

Michael Doubez said:
Hello,

I have the following minimal code:

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
There is no N in the template declration :
template<class T >
 
M

Michael Doubez

I have the following minimal code:
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.
 
P

Paul

I have the following minimal code:
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 instanciation because:
static int const N = T::size. /*This does not instanciate the template.*/

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*/
Note: afaik the ( ={} ) doesn't do anything and is not required.
ok now it knows that N is T::size:
static int values[N]; /* --- N is T::size. ---*/

AFAICT you do not need:
template< class T > int const Foo<T>::N; /* This is instanciated implicitly
with Foo<Bar> f anyway*/


HTH
 
M

Michael Doubez

I have the following minimal code:
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.
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.

The only work done by the compiler at this point is syntax checking.
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.
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).
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.
 
P

Paul

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;
};
 
M

Michael Doubez

--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;
};

Try it out:
#include <iostream>

struct Foo
{
// link fails with this line
static int const N = 42;
// compiles with this line
// enum { N = 42 };
};


void print( int const & v ) {
std::cout<<v<<'\n';
}

int main() {
print(Foo::N);
}
 
P

Paul

Michael Doubez said:
Try it out:
#include <iostream>
struct Foo{
// link fails with this line
static int const N = 42;
// compiles with this line
// enum { N = 42 };
};

void print( int const & v ) { std::cout<<v<<'\n';}

int main() {
print(Foo::N);
}

I did try it out after I posted, and it worked just fine, what compiler did
you say to avoid again? :)
Here's the code I posted , maybe that compile of yours isn't too good.

struct Bar1 { enum { size = 42 } ;};
struct Bar2 { enum { size = 52 } ;};

template< class T >
struct Foo{
static int const N= T::size;
static int values[N];
public:
Foo(){}
template<typename U>
void func(Foo<U>& para);
};

//template< class T > int const Foo<T>::N;
template<class T> int Foo<T>::values[Foo<T>::N]={0};

template<typename T>
template<typename U>
void Foo<T>::func(Foo<U>& para){
std::cout<< "\nthis->N: \t" << this->N;
std::cout<< "\nthat.N: \t" << para.N;
}

void funct(int const & r){
std::cout<<"\nIn funct, value of parameter = " << r;
}

int main() {
Foo<Bar1> f1;
Foo<Bar2> f2;

funct(f1.N);
funct(f2.N);
f1.func(f2);
f2.func(f1);
}


I also tried another couple of ways to use it as a reference and all seemed
to compile ok with Microsoft v14 command line compiler.

And I don't think that version is too good because i have had problems with
it compiling templates in the form of :
template<template<class T> class U, classT>
When I go onto my newer VS2010 these compile just fine , so that old command
line compiler I've been using to test your code isn't the greatest but it
seems to compile/link your code fine without
template< class T > int const Foo<T>::N;


HTH
 
M

Michael Doubez

Try it out:
#include <iostream>
struct Foo{
   // link fails with this line
   static int const N = 42;
   // compiles with this line
   // enum { N = 42 };
};
void print( int const & v ) { std::cout<<v<<'\n';}
int main() {
   print(Foo::N);
}

I did try it out after I posted, and it worked just fine, what compiler did
you say to avoid again?  :) [snip]
When I go onto my newer VS2010 these compile just fine , so that old command
line compiler I've been using to test your code isn't the greatest but it
seems to compile/link your code fine without
template< class T > int const Foo<T>::N;

It doesn't compile on codepad (And comeau doesn't link so it is of no
help here).
It doesn't either on gcc (up to 4.3.3)

The standard is quite clear on the subject with the one definition
rule and a static data member is always a declaration within the class
scope (§9.4.2/2)
"The declaration of a /static/ data member in its class is not a
definition [...].

And more precisely §9.4.2/4:
"If a static data member is of const integral or const enumeration
type, its declaration in the class
definition can specify a constant-initializer which shall be an
integral constant expression (5.19). In that
case, the member can appear in integral constant expressions. *The
member shall still be defined in a namespace scope if it is used in
the program* and the namespace scope definition shall not contain an
initializer."

This requirement still holds in the proposal for c++0x.

It may be a compiler extension or perhaps they don't consider this as
"used in the program" (the value is used but not the variable, it
could make sense).
 
P

Paul

Michael Doubez said:
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;
};
Try it out:
#include <iostream>
struct Foo{
// link fails with this line
static int const N = 42;
// compiles with this line
// enum { N = 42 };
};
void print( int const & v ) { std::cout<<v<<'\n';}
int main() {
print(Foo::N);
}

I did try it out after I posted, and it worked just fine, what compiler
did
you say to avoid again? :) [snip]
When I go onto my newer VS2010 these compile just fine , so that old
command
line compiler I've been using to test your code isn't the greatest but it
seems to compile/link your code fine without
template< class T > int const Foo<T>::N;

It doesn't compile on codepad (And comeau doesn't link so it is of no
help here).
It doesn't either on gcc (up to 4.3.3)

The standard is quite clear on the subject with the one definition
rule and a static data member is always a declaration within the class
scope (§9.4.2/2)
"The declaration of a /static/ data member in its class is not a
definition [...].

And more precisely §9.4.2/4:
"If a static data member is of const integral or const enumeration
type, its declaration in the class
definition can specify a constant-initializer which shall be an
integral constant expression (5.19). In that
case, the member can appear in integral constant expressions. *The
member shall still be defined in a namespace scope if it is used in
the program* and the namespace scope definition shall not contain an
initializer."

This requirement still holds in the proposal for c++0x.

It may be a compiler extension or perhaps they don't consider this as
"used in the program" (the value is used but not the variable, it
could make sense).

It thats what it says then it must be true.

It still compiles fine if I use it like this:
funct(Foo<Bar1>::N);


So amybe there is something wrong with my compiler then . :)
 
J

James Kanze

I have the following minimal code:
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.

This may have been changed in more recent versions of the draft,
but at least in the current standard, the problem is that
template< class T > int const Foo<T>::N;
is NOT a definition, but only a declaration. When defining data
in this manner, the declaration is only a definition if it
contains an initializer. You should probably try:
template< class T > int const Foo<T>::N = T::size;
(and drop the = T::size in the class definition).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top