Problem with static data members in templated class

A

Andy

Hi,

I have some code (below) that is giving me problems to compile. I'm
using
GCC 4.4, and have already posed this question on comp.gcc.help, but
am
trying here also in case someone here has had a similar problem and
knows
the solution.

The code is as follows:


template <char* T>
struct C1
{
C1() { }
/*C1(const C1& other) = delete;*/

char operator()()
{ return *T; }
};

struct C2
{
static char m;
static C1<&m> op;
};

template <typename T>
struct C3
{
static char m;
static C1<&m> op1;
static C1<&C2::m> op2;
};

// Initialise C2::m and C2::eek:p
char C2::m = 'a';
C1<&C2::m> C2::eek:p;

// Initialise C3::m for type int
/** This line fails in Comeau C++ 4.3, but is ok in GCC 4.4 **/
template<> char C3<int>::m = 'b';

// Initialise C3::eek:p2 for type int
template <typename T> C1<&C2::m> C3<T>::eek:p2;
template C1<&C2::m> C3<int>::eek:p2;

// Initialise C2::eek:p1 for type int
#if 0
/** This doesn't compile in GCC 4.4 but does in Comeau C++ 4.3 **/
template <typename T> C1<&C3<T>::m> C3<T>::eek:p1;
template C1<&C3<int>::m> C3<int>::eek:p1;
#elif 0
/** This compiles, but doesn't link **/
template<>
C1<&C3<int>::m> C3<int>::eek:p1;
#else
/** This compiles and links but requires the copy-constructor - bad!
**/
template<>
C1<&C3<int>::m> C3<int>::eek:p1 = C1<&C3<int>::m>();
#endif

int main()
{
printf("%c\n", C2::eek:p());
printf("%c\n", C3<int>::eek:p1());
printf("%c\n", C3<int>::eek:p2());
return 0;
}


The problem is in compiling the lines which deal with the
initialisation of
C3<int>::eek:p1. The syntax that works (i.e. the third one) uses the
copy
constructor, but I really want to be able to disable the copy
constructor in
C1 in the final implementation. I should have thought that the first
syntax
should have worked, i.e.:

template <typename T> C1<&C3<T>::m> C3<T>::eek:p1;
template C1<&C3<int>::m> C3<int>::eek:p1;

but I get the error:

error: conflicting declaration 'C1<(& C3::m)> C3<T>::eek:p1'
error: 'C3<T>::eek:p1' has a previous declaration as 'C1<(& C3<T>::m)>
C3<T>::eek:p1'
error: declaration of 'C1<(& C3<T>::m)> C3<T>::eek:p1' outside of class
is not
definition

Syntax two compiles, but the linker fails with:

undefined reference to `C3<int>::eek:p1'


Out of interest I tried the Comeau online test compiler, and it has
no
problem with the declaration of C3<int>::eek:p1, but does have an error
with
the declaration of C3<int>::m!

I expect I'm missing a 'template' or 'typename' keyword somewhere
obvious.

If someone is able to help me out, I would be most grateful.

Thanks
Andy
 
V

Vladimir Jovic

Andy said:
Hi,

I have some code (below) that is giving me problems to compile. I'm
using
GCC 4.4, and have already posed this question on comp.gcc.help, but
am
trying here also in case someone here has had a similar problem and
knows
the solution.

The code is as follows:


template <char* T>
struct C1
{
C1() { }
/*C1(const C1& other) = delete;*/

char operator()()
{ return *T; }
};

struct C2
{
static char m;
static C1<&m> op;
};

template <typename T>
struct C3
{
static char m;
static C1<&m> op1;


Change the line above to :
static C1< &C3<T>::m > op1;
and it should compile.
 
A

Andy

Andy said:
Hi, [... snip ...]

template <typename T>
struct C3
  {
  static char m;
  static C1<&m> op1;

Change the line above to :
static C1< &C3<T>::m > op1;
and it should compile.

Thank you, but unfortunately, I still get the same error:

error: conflicting declaration ‘C1<(& C3::m)> C3<T>::eek:p1’
error: ‘C3<T>::eek:p1’ has a previous declaration as ‘C1<(& C3<T>::m)>
C3<T>::eek:p1’
error: declaration of ‘C1<(& C3<T>::m)> C3<T>::eek:p1’ outside of class
is not definition

Regards,

Andy
 
V

Vladimir Jovic

Andy said:
Andy said:
Hi, [... snip ...]

template <typename T>
struct C3
{
static char m;
static C1<&m> op1;
Change the line above to :
static C1< &C3<T>::m > op1;
and it should compile.

Thank you, but unfortunately, I still get the same error:

error: conflicting declaration ‘C1<(& C3::m)> C3<T>::eek:p1’
error: ‘C3<T>::eek:p1’ has a previous declaration as ‘C1<(& C3<T>::m)>
C3<T>::eek:p1’
error: declaration of ‘C1<(& C3<T>::m)> C3<T>::eek:p1’ outside of class
is not definition

Really strange. I copy&paste your code, made this modification, and it
compiled. Are you sure you tried it on the code you posted?


I am not sure if it matters, but I used g++ 4.3.0 and I compiled like this :

[vladimir@juniper data_create]$ g++ tgf.cpp -Wall -ansi
[vladimir@juniper data_create]$ ./a.out
a
b
a
[vladimir@juniper data_create]$
 
A

Andy

Really strange. I copy&paste your code, made this modification, and it
compiled. Are you sure you tried it on the code you posted?

I am not sure if it matters, but I used g++ 4.3.0 and I compiled like this :

[vladimir@juniper data_create]$ g++ tgf.cpp -Wall -ansi
[vladimir@juniper data_create]$ ./a.out
a
b
a
[vladimir@juniper data_create]$

Thank you for continuing to look at this one for me.

Sorry, I think I did not explain correctly in my original post: the
code as posted compiles, links and runs correctly, but it uses the
copy constructor of class C1 in initialisation. In my final
implementation I need to be able to disable the copy constructor in
C1.

Here is the code, with your modification, but with the initialiser not
using the copy constructor (i.e. I've changed the "#if 0" to a "#if
1", and I've disabled it in C1):

#include <stdio.h>

template <char* T>
struct C1
{
C1() { }
C1(const C1& other) = delete;

char operator()()
{ return *T; }
};

struct C2
{
static char m;
static C1<&m> op;
};

template <typename T>
struct C3
{
static char m;
static C1<&C3<T>::m> op1;
static C1<&C2::m> op2;
};

// Initialise C2::m and C2::eek:p
char C2::m = 'a';
C1<&C2::m> C2::eek:p;

// Initialise C3::m for type int
/** This line fails in Comeau C++ 4.3, but is ok in GCC 4.4 **/
template<> char C3<int>::m = 'b';

// Initialise C3::eek:p2 for type int
template <typename T> C1<&C2::m> C3<T>::eek:p2;
template C1<&C2::m> C3<int>::eek:p2;

// Initialise C2::eek:p1 for type int
#if 1
/** This doesn't compile in GCC 4.4 but does in Comeau C++ 4.3 **/
template <typename T> C1<&C3<T>::m> C3<T>::eek:p1;
template C1<&C3<int>::m> C3<int>::eek:p1;
#elif 0
/** This compiles, but doesn't link **/
template<>
C1<&C3<int>::m> C3<int>::eek:p1;
#else
/** This compiles and links but requires the copy-constructor - bad!
**/
template<>
C1<&C3<int>::m> C3<int>::eek:p1 = C1<&C3<int>::m>();
#endif

int main()
{
printf("%c\n", C2::eek:p());
printf("%c\n", C3<int>::eek:p1());
printf("%c\n", C3<int>::eek:p2());
return 0;
}


When I compile, I get:

$ g++ 1.cpp -Wall -ansi -std=c++0x
1.cpp:42: error: conflicting declaration ‘C1<(& C3::m)> C3<T>::eek:p1’
1.cpp:23: error: ‘C3<T>::eek:p1’ has a previous declaration as ‘C1<(&
C3<T>::m)> C3<T>::eek:p1’
1.cpp:42: error: declaration of ‘C1<(& C3<T>::m)> C3<T>::eek:p1’ outside
of class is not definition


If you are able to get this to compile in GCC 4.3, I will explore
whether this is a bug that's crept into gcc 4.4.

Many, many thanks
Andy
 
F

Fabrizio J Bonsignore

You are still assuming the compiler is written good faithed and
reasonably perfect, so you just have to move your code around and it
will work without flaws because there is a strong logic behind the
full matter and... but that is ideal Reality. I am having trouble with
copy contructors and global (static) variables as well. See the thread
I opened about this issue. It also seems that some people can compile
it without problem! But others cannot. THEN it also seems after the
issue is acknowledged they no longer can compile it normally! I
dislike MYSTERIES in these matters because they are unnecessary, but
some people MAY feel compelled to produce such overwhelming mysteries
even HERE. I DO think there should be by now C++, D++, ... M++, ... X+
+ variants of the language different from each other rather than a
moving target, sliding rule language. The solution with copy
constructors seems to be a rather complex mix of const and definitions
and ommission; the solution to (static) global variables seems to be
reinitialization within main() scope. Options and flags may affect but
that is also a problem that should be bundled and not take too much
time. Previous definition of templates and instantiations may be
causing trouble too, even when they are expected to be independent
from copy constructor declarations and global/static data variables.
This all takes time to establish and solve...

Danilo J Bonsignore
 
A

Alf P. Steinbach /Usenet

* Andy, on 04.10.2010 14:11:
I have some code (below) that is giving me problems to compile.

Yes, I suspect your code is invalid even when corrected for syntax etc., but
although Comeau Online agrees with that sentiment I'm not 100% sure -- using
one static member of a class template to initialize another just could be
allowed, or not explicitly disallowed, or it could be like static initialization
order fiasco (some hours of study might resolve the question).

Anyway, fixing up the code it caused an internal compiler error in MinGW g++
4.4.1. I've reported that at <url:
https://sourceforge.net/tracker/?func=detail&aid=3081134&group_id=200665&atid=974439>.
So it seems you were battling three hostile forces at the same time: a design
problem, a corner case or murky territory of the Holy Standard, and a g++
compiler bug. :)

You can probably use the following workaround, but I suggest looking hard at
your design, thinking about what you're trying to achieve, how responsibilities
should be allocated, and what knowledge is needed where to do things:


<code>
#include <stdio.h>

template< char* p >
struct C1
{
char operator()() const { return *p; }
};

struct C2
{
static char m;
static C1<&m> op;
};

char C2::m = 'a';
C1<&C2::m> C2::eek:p;

template< class T >
struct C3m
{
static char m;
};

template< class T > char C3m<T>::m = '0';
template<> char C3m<int>::m = 'b';

template< class T >
struct C3
{
static C1<&C3m<T>::m> op1;
static C1<&C2::m> op2;
};

template< class T > C1<&C3m<T>::m> C3<T>::eek:p1;
template<> C1<&C3m<int>::m> C3<int>::eek:p1;

template< class T > C1<&C2::m> C3<T>::eek:p2;
template<> C1<&C2::m> C3<int>::eek:p2;

int main()
{
printf("%c\n", C2::eek:p());
printf("%c\n", C3<int>::eek:p1());
printf("%c\n", C3<int>::eek:p2());
}
</code>


Cheers & hth.,

- Alf
 
J

James Kanze

* Andy, on 04.10.2010 14:11:

[...]
Yes, I suspect your code is invalid even when corrected for
syntax etc., but although Comeau Online agrees with that
sentiment I'm not 100% sure -- using one static member of
a class template to initialize another just could be allowed,
or not explicitly disallowed, or it could be like static
initialization order fiasco (some hours of study might resolve
the question).

The order of initialization of static member variables of an
instantiation of a class template is undefined.
 
A

Andy

* Andy, on 04.10.2010 14:11:


Yes, I suspect your code is invalid even when corrected for syntax etc., but
although Comeau Online agrees with that sentiment I'm not 100% sure  --  using
one static member of a class template to initialize another just could be
allowed, or not explicitly disallowed, or it could be like static initialization
order fiasco (some hours of study might resolve the question).

I think it is ok. I'm not using the *value* of static member to
initialise another static member, but a *pointer* to that first static
member which I believe should be automatically determined and not
subject to any initialisation order. Is this not right?
Anyway, fixing up the code it caused an internal compiler error in MinGW g++
4.4.1. I've reported that at <url:https://sourceforge.net/tracker/?func=detail&aid=3081134&group_id=200...>.
So it seems you were battling three hostile forces at the same time: a design
problem, a corner case or murky territory of the Holy Standard, and a g++
compiler bug. :)

Yes, it seems so! Having done some testing around, it appears there
is indeed a bug in the 4.4 branch; I've downloaded and installed the
head (experimental) revision of gcc 4.6.0 and my original code
compiles and runs correctly, so the bug, if that's what it is, may
well be solved.

In contrast, I'm afraid the code you've submitted to the mingw bug
tracker, creates the following error:

$ ../bin/g++ x.cpp -std=gnu++0x
x.cpp:27:42: error: duplicate initialization of ‘C3<int>::m’
x.cpp:27:42: error: redefinition of ‘char C3<int>::m’
x.cpp:26:33: error: ‘char C3<int>::m’ previously declared here
/tmp/ccHxiKB8.o: In function `main':
x.cpp:(.text+0x2b): undefined reference to `C3<int>::eek:p1'
x.cpp:(.text+0x4a): undefined reference to `C3<int>::eek:p2'


Here is a diff of the changes needed to your code to get it to compile
and run correctly at 4.6.0, in case you wish to update your bug
submission:

@@ -23,14 +23,13 @@
static C1<&C2::m> op2;
};

-template< class T > char C3<T>::m = '0';
template<> char C3<int>::m = 'b';

template< class T > C1<&C3<T>::m> C3<T>::eek:p1; // MinGW g++ 4.4.1
ICE
-template<> C1<&C3<int>::m> C3<int>::eek:p1;
+template C1<&C3<int>::m> C3<int>::eek:p1;

template< class T > C1<&C2::m> C3<T>::eek:p2;
-template<> C1<&C2::m> C3<int>::eek:p2;
+template C1<&C2::m> C3<int>::eek:p2;

int main()
{


Cheers
Andy
 
A

Alf P. Steinbach /Usenet

* Andy, on 05.10.2010 14:26:
I think it is ok. I'm not using the *value* of static member to
initialise another static member, but a *pointer* to that first static
member which I believe should be automatically determined and not
subject to any initialisation order. Is this not right?

Not exactly. The type and hence in general case size and address of one static
member of S depends on the address of a static member of S. In a more general
scenario the type and hence address of that second static member could be
specified to depend on the address of the first. Since a "human compiler" can
easily resolve that it's not an unsurmountable problem. But it's a question of
whether the standard requires support for this, or allows the compiler to reject
the code, or something in between (like static initialization fiasco).

Yes, it seems so! Having done some testing around, it appears there
is indeed a bug in the 4.4 branch;

No, when a compiler crashes with an internal compiler error (ICE) then there's
no question of bug or not, there's absolutely no room for "it appears there is".

An ICE is a bug by definition.

And it generally receives highest priority in bug-fixing: there's no more buggy
bug than this.

I've downloaded and installed the
head (experimental) revision of gcc 4.6.0 and my original code
compiles and runs correctly, so the bug, if that's what it is, may
well be solved.

In contrast, I'm afraid the code you've submitted to the mingw bug
tracker, creates the following error:

$ ../bin/g++ x.cpp -std=gnu++0x
x.cpp:27:42: error: duplicate initialization of ‘C3<int>::m’
x.cpp:27:42: error: redefinition of ‘char C3<int>::m’
x.cpp:26:33: error: ‘char C3<int>::m’ previously declared here
/tmp/ccHxiKB8.o: In function `main':
x.cpp:(.text+0x2b): undefined reference to `C3<int>::eek:p1'
x.cpp:(.text+0x4a): undefined reference to `C3<int>::eek:p2'


Here is a diff of the changes needed to your code to get it to compile
and run correctly at 4.6.0, in case you wish to update your bug
submission:

@@ -23,14 +23,13 @@
static C1<&C2::m> op2;
};

-template< class T> char C3<T>::m = '0';
template<> char C3<int>::m = 'b';

template< class T> C1<&C3<T>::m> C3<T>::eek:p1; // MinGW g++ 4.4.1
ICE
-template<> C1<&C3<int>::m> C3<int>::eek:p1;
+template C1<&C3<int>::m> C3<int>::eek:p1;

template< class T> C1<&C2::m> C3<T>::eek:p2;
-template<> C1<&C2::m> C3<int>::eek:p2;
+template C1<&C2::m> C3<int>::eek:p2;

int main()
{

Actually it was your code, not mine. :) Just fixed.

But no I don't think there's any point in transforming the code to more valid
form. The point is that the code exemplifies an internal compiler bug. Whether
the code is valid is irrelevant.


Cheers,

- Alf
 
J

Joshua Maurice

* Andy, on 05.10.2010 14:26:


No, when a compiler crashes with an internal compiler error (ICE) then there's
no question of bug or not, there's absolutely no room for "it appears there is".

An ICE is a bug by definition.

And it generally receives highest priority in bug-fixing: there's no more buggy
bug than this.

I believe hope the opposite. I can work around compiler crashes with
relative ease, depending on its exact nature. However, working around
a subtle bug of which the compiler produces the wrong binary output,
now that is the worst kind of bug. Silent failures in compilation in a
general sense are worse than loud obvious failures.
 
A

Alf P. Steinbach /Usenet

* Joshua Maurice, on 05.10.2010 19:19:
I believe hope the opposite. I can work around compiler crashes with
relative ease, depending on its exact nature. However, working around
a subtle bug of which the compiler produces the wrong binary output,
now that is the worst kind of bug. Silent failures in compilation in a
general sense are worse than loud obvious failures.

An ICE means that some basic assumptions in the compiler are wrong.

That means first of all that it can produce incorrect results when it doesn't crash.

It also means that other bugfixes can be foiled.

It's the same as with an assert kicking in in your own program.

It means that the foundations, the assumptions, are shaky, and that needs fixing
first of all. No point in putting in new better distortion-free glass panes in a
building that's not well founded. At least in an earthquake area.


Cheers,

- Alf
 

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,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top