By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
458,083 Members | 1,254 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 458,083 IT Pros & Developers. It's quick & easy.

Instantiation of static variable in template class fails, why??

P: n/a

Hi, I really need some help here. After upgrading to g++ 3.4 I have run
into all sorts of troubles that I'm sure depends on my lack of proper
understanding of C++. I would now like to get it right once and for all,
if possible.

Most severe is the problem illustrated by the code below. It's based on
the "pluggable factory pattern" described in

http://www.adtmag.com/joop/crarticle.asp?ID=1520

It's a generic factory class using templates to ease the addition of new
types to the hierarchy.

The code below is a simplified version of the system we use. It compiles
and runs fine and dandy with g++ 3.3.3, whereas 3.4.2 gives the error
message "too few template-parameter-lists". If I uncomment the
commented lines below (//template <>) both versions compile the code but
neither run properly - no output is produced. It has something to
do with instantiation of static members in template classes, AFAIU.
Expected output from the program is

DataTypeA
DataTypeB

Could someone please help me understand what's going on here? I'd be very
grateful. Code and some output below.

Thanks,
Erik

Successful run with g++ 3.3.3:

bash$ g++ -c main.cc
bash$ g++ -c generalmaker.cc
bash$ g++ -c typeAmaker.cc
bash$ g++ -c typeBmaker.cc
bash$ g++ -o makertest main.o generalmaker.o typeAmaker.o typeBmaker.o
bash$ ./makertest
DataTypeA
DataTypeB
bash$

Code:

-----8<-----
//main.cc

#include <iostream>
#include "generalmaker.h"
#include <string>
#include <list>

using namespace std;

int main() {

list<string> typelist = GeneralMaker::listRegistered();

for( list<string>::iterator it = typelist.begin(); it != typelist.end(); ++it ) {
cerr<<*it<<endl;
}

return 0;
}

-----8<-----
//generalmaker.h

#ifndef GENERALMAKER_H
#define GENERALMAKER_H

#include <map>
#include <string>
#include <list>

class GeneralData;

class GeneralMaker
{
public:
virtual ~GeneralMaker() {}
static GeneralData * newData( const std::string& );
static std::list<std::string> listRegistered();
protected:
GeneralMaker( ) {}
virtual GeneralData* makeData() = 0;
typedef std::map< std::string , GeneralMaker * > MakerMap;
static MakerMap & registry();

};

template <class Data, const char * str_>
class GeneralMakerTP : public GeneralMaker
{
protected:
GeneralMakerTP() : GeneralMaker( )
{
Data d;
std::string str = d.uniqueName();
GeneralMaker::registry().insert( make_pair( str, this ) );
}
GeneralData* makeData()
{
return new Data();
}
static const GeneralMakerTP<Data, str_> registerThis;
};

#endif

-----8<-----
//generalmaker.cc

#include "generalmaker.h"
#include "generaldata.h"

using namespace std;

list<string> GeneralMaker::listRegistered()
{
list<string> list;

for ( MakerMap::const_iterator it = registry().begin(); it != registry().end(); ++it )
{
list.push_back( it->first );
}
return list;
}

GeneralMaker::MakerMap & GeneralMaker::registry()
{
/* We use this because static instances of sub classes of GeneralMaker
make use of this map. The idea comes from c++ faq lite
[10.12] How do I prevent the "static initialization order fiasco"?
*/

static GeneralMaker::MakerMap reg;
return reg;
}

GeneralData* GeneralMaker::newData( const std::string& generalDataType )
{
GeneralMaker* maker =
(*registry().find( generalDataType )).second;
return maker ? maker->makeData() : NULL;
}

-----8<-----
//generaldata.h

#ifndef GENERALDATA_H
#define GENERALDATA_H

#include <string>
#include "generalmaker.h"
#include "generaldata.h"
class GeneralData
{
public:
GeneralData() {}
virtual ~GeneralData(){}

virtual std::string uniqueName() = 0;

};

#endif

-----8<-----
//datatypeA.h

#ifndef DATATYPEA_H
#define DATATYPEA_H

#include "generaldata.h"
#include <string>

class DataTypeA : public GeneralData
{
public:
DataTypeA( ) : GeneralData() {}
~DataTypeA() {}
std::string uniqueName() { return std::string("DataTypeA"); }

};

#endif

-----8<-----
//datatypeB.h

#ifndef DATATYPEB_H
#define DATATYPEB_H

#include "generaldata.h"
#include <string>

class DataTypeB : public GeneralData
{
public:
DataTypeB( ) : GeneralData() {}
~DataTypeB() {}
std::string uniqueName() { return std::string("DataTypeB"); }

};

#endif

-----8<-----
//typeAmaker.cc

#include "datatypeA.h"
#include "generalmaker.h"

char nameA[] = "DataTypeA";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;

-----8<-----
//typeBmaker.cc

#include "datatypeB.h"
#include "generalmaker.h"

char nameB[] = "DataTypeB";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeB, nameB> GeneralMakerTP<DataTypeB,nameB>::registerThis;
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #1
Share this Question
Share on Google+
18 Replies


P: n/a
Maybe (and I dont claim to be a guru) templates are generated code and
static members cause ambiguity, because they need to be specifically called
and 'typed'.
I had same problem and once I found a way around it, all hell came loose
when I tried to import/export.
So I removed the static members from the template and I called them in the
derived classes of the template
for example
class MyDerivedFromTemplate : public YourTemplate< SomeClass >
{
static int s_number_of_objects;
.....
};

hope this helps.

"Erik Arner" <er********@hotmail.com> wrote in message
news:pa****************************@hotmail.com...

Hi, I really need some help here. After upgrading to g++ 3.4 I have run
into all sorts of troubles that I'm sure depends on my lack of proper
understanding of C++. I would now like to get it right once and for all,
if possible.

Most severe is the problem illustrated by the code below. It's based on
the "pluggable factory pattern" described in

http://www.adtmag.com/joop/crarticle.asp?ID=1520

It's a generic factory class using templates to ease the addition of new
types to the hierarchy.

The code below is a simplified version of the system we use. It compiles
and runs fine and dandy with g++ 3.3.3, whereas 3.4.2 gives the error
message "too few template-parameter-lists". If I uncomment the
commented lines below (//template <>) both versions compile the code but
neither run properly - no output is produced. It has something to
do with instantiation of static members in template classes, AFAIU.
Expected output from the program is

DataTypeA
DataTypeB

Could someone please help me understand what's going on here? I'd be very
grateful. Code and some output below.

Thanks,
Erik

Successful run with g++ 3.3.3:

bash$ g++ -c main.cc
bash$ g++ -c generalmaker.cc
bash$ g++ -c typeAmaker.cc
bash$ g++ -c typeBmaker.cc
bash$ g++ -o makertest main.o generalmaker.o typeAmaker.o typeBmaker.o
bash$ ./makertest
DataTypeA
DataTypeB
bash$

Code:

-----8<-----
//main.cc

#include <iostream>
#include "generalmaker.h"
#include <string>
#include <list>

using namespace std;

int main() {

list<string> typelist = GeneralMaker::listRegistered();

for( list<string>::iterator it = typelist.begin(); it != typelist.end();
++it ) {
cerr<<*it<<endl;
}

return 0;
}

-----8<-----
//generalmaker.h

#ifndef GENERALMAKER_H
#define GENERALMAKER_H

#include <map>
#include <string>
#include <list>

class GeneralData;

class GeneralMaker
{
public:
virtual ~GeneralMaker() {}
static GeneralData * newData( const std::string& );
static std::list<std::string> listRegistered();
protected:
GeneralMaker( ) {}
virtual GeneralData* makeData() = 0;
typedef std::map< std::string , GeneralMaker * > MakerMap;
static MakerMap & registry();

};

template <class Data, const char * str_>
class GeneralMakerTP : public GeneralMaker
{
protected:
GeneralMakerTP() : GeneralMaker( )
{
Data d;
std::string str = d.uniqueName();
GeneralMaker::registry().insert( make_pair( str, this ) );
}
GeneralData* makeData()
{
return new Data();
}
static const GeneralMakerTP<Data, str_> registerThis;
};

#endif

-----8<-----
//generalmaker.cc

#include "generalmaker.h"
#include "generaldata.h"

using namespace std;

list<string> GeneralMaker::listRegistered()
{
list<string> list;

for ( MakerMap::const_iterator it = registry().begin(); it !=
registry().end(); ++it )
{
list.push_back( it->first );
}
return list;
}

GeneralMaker::MakerMap & GeneralMaker::registry()
{
/* We use this because static instances of sub classes of GeneralMaker
make use of this map. The idea comes from c++ faq lite
[10.12] How do I prevent the "static initialization order fiasco"?
*/

static GeneralMaker::MakerMap reg;
return reg;
}

GeneralData* GeneralMaker::newData( const std::string& generalDataType )
{
GeneralMaker* maker =
(*registry().find( generalDataType )).second;
return maker ? maker->makeData() : NULL;
}

-----8<-----
//generaldata.h

#ifndef GENERALDATA_H
#define GENERALDATA_H

#include <string>
#include "generalmaker.h"
#include "generaldata.h"
class GeneralData
{
public:
GeneralData() {}
virtual ~GeneralData(){}

virtual std::string uniqueName() = 0;

};

#endif

-----8<-----
//datatypeA.h

#ifndef DATATYPEA_H
#define DATATYPEA_H

#include "generaldata.h"
#include <string>

class DataTypeA : public GeneralData
{
public:
DataTypeA( ) : GeneralData() {}
~DataTypeA() {}
std::string uniqueName() { return std::string("DataTypeA"); }

};

#endif

-----8<-----
//datatypeB.h

#ifndef DATATYPEB_H
#define DATATYPEB_H

#include "generaldata.h"
#include <string>

class DataTypeB : public GeneralData
{
public:
DataTypeB( ) : GeneralData() {}
~DataTypeB() {}
std::string uniqueName() { return std::string("DataTypeB"); }

};

#endif

-----8<-----
//typeAmaker.cc

#include "datatypeA.h"
#include "generalmaker.h"

char nameA[] = "DataTypeA";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeA, nameA>
GeneralMakerTP<DataTypeA,nameA>::registerThis;

-----8<-----
//typeBmaker.cc

#include "datatypeB.h"
#include "generalmaker.h"

char nameB[] = "DataTypeB";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeB, nameB>
GeneralMakerTP<DataTypeB,nameB>::registerThis;
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #2

P: n/a
Sorry, you may ask but I want to use the static data in the base template.
You use functions (they may have to be virtual) to return that data, but in
the derived class you make it return the appropriate static data.
In a way the base class is dealing with data that the derived class is going
to set. Lovely OOP.
Cheers.

"Charlie" <du*********@yahoo.com> wrote in message
news:nk*****************@news-server.bigpond.net.au...
Maybe (and I dont claim to be a guru) templates are generated code and
static members cause ambiguity, because they need to be specifically
called and 'typed'.
I had same problem and once I found a way around it, all hell came loose
when I tried to import/export.
So I removed the static members from the template and I called them in the
derived classes of the template
for example
class MyDerivedFromTemplate : public YourTemplate< SomeClass >
{
static int s_number_of_objects;
.....
};

hope this helps.

"Erik Arner" <er********@hotmail.com> wrote in message
news:pa****************************@hotmail.com...

Hi, I really need some help here. After upgrading to g++ 3.4 I have run
into all sorts of troubles that I'm sure depends on my lack of proper
understanding of C++. I would now like to get it right once and for all,
if possible.

Most severe is the problem illustrated by the code below. It's based on
the "pluggable factory pattern" described in

http://www.adtmag.com/joop/crarticle.asp?ID=1520

It's a generic factory class using templates to ease the addition of new
types to the hierarchy.

The code below is a simplified version of the system we use. It compiles
and runs fine and dandy with g++ 3.3.3, whereas 3.4.2 gives the error
message "too few template-parameter-lists". If I uncomment the
commented lines below (//template <>) both versions compile the code but
neither run properly - no output is produced. It has something to
do with instantiation of static members in template classes, AFAIU.
Expected output from the program is

DataTypeA
DataTypeB

Could someone please help me understand what's going on here? I'd be very
grateful. Code and some output below.

Thanks,
Erik

Successful run with g++ 3.3.3:

bash$ g++ -c main.cc
bash$ g++ -c generalmaker.cc
bash$ g++ -c typeAmaker.cc
bash$ g++ -c typeBmaker.cc
bash$ g++ -o makertest main.o generalmaker.o typeAmaker.o typeBmaker.o
bash$ ./makertest
DataTypeA
DataTypeB
bash$

Code:

-----8<-----
//main.cc

#include <iostream>
#include "generalmaker.h"
#include <string>
#include <list>

using namespace std;

int main() {

list<string> typelist = GeneralMaker::listRegistered();

for( list<string>::iterator it = typelist.begin(); it != typelist.end();
++it ) {
cerr<<*it<<endl;
}

return 0;
}

-----8<-----
//generalmaker.h

#ifndef GENERALMAKER_H
#define GENERALMAKER_H

#include <map>
#include <string>
#include <list>

class GeneralData;

class GeneralMaker
{
public:
virtual ~GeneralMaker() {}
static GeneralData * newData( const std::string& );
static std::list<std::string> listRegistered();
protected:
GeneralMaker( ) {}
virtual GeneralData* makeData() = 0;
typedef std::map< std::string , GeneralMaker * > MakerMap;
static MakerMap & registry();

};

template <class Data, const char * str_>
class GeneralMakerTP : public GeneralMaker
{
protected:
GeneralMakerTP() : GeneralMaker( )
{
Data d;
std::string str = d.uniqueName();
GeneralMaker::registry().insert( make_pair( str, this ) );
}
GeneralData* makeData()
{
return new Data();
}
static const GeneralMakerTP<Data, str_> registerThis;
};

#endif

-----8<-----
//generalmaker.cc

#include "generalmaker.h"
#include "generaldata.h"

using namespace std;

list<string> GeneralMaker::listRegistered()
{
list<string> list;

for ( MakerMap::const_iterator it = registry().begin(); it !=
registry().end(); ++it )
{
list.push_back( it->first );
}
return list;
}

GeneralMaker::MakerMap & GeneralMaker::registry()
{
/* We use this because static instances of sub classes of GeneralMaker
make use of this map. The idea comes from c++ faq lite
[10.12] How do I prevent the "static initialization order fiasco"?
*/

static GeneralMaker::MakerMap reg;
return reg;
}

GeneralData* GeneralMaker::newData( const std::string& generalDataType )
{
GeneralMaker* maker =
(*registry().find( generalDataType )).second;
return maker ? maker->makeData() : NULL;
}

-----8<-----
//generaldata.h

#ifndef GENERALDATA_H
#define GENERALDATA_H

#include <string>
#include "generalmaker.h"
#include "generaldata.h"
class GeneralData
{
public:
GeneralData() {}
virtual ~GeneralData(){}

virtual std::string uniqueName() = 0;

};

#endif

-----8<-----
//datatypeA.h

#ifndef DATATYPEA_H
#define DATATYPEA_H

#include "generaldata.h"
#include <string>

class DataTypeA : public GeneralData
{
public:
DataTypeA( ) : GeneralData() {}
~DataTypeA() {}
std::string uniqueName() { return std::string("DataTypeA"); }

};

#endif

-----8<-----
//datatypeB.h

#ifndef DATATYPEB_H
#define DATATYPEB_H

#include "generaldata.h"
#include <string>

class DataTypeB : public GeneralData
{
public:
DataTypeB( ) : GeneralData() {}
~DataTypeB() {}
std::string uniqueName() { return std::string("DataTypeB"); }

};

#endif

-----8<-----
//typeAmaker.cc

#include "datatypeA.h"
#include "generalmaker.h"

char nameA[] = "DataTypeA";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeA, nameA>
GeneralMakerTP<DataTypeA,nameA>::registerThis;

-----8<-----
//typeBmaker.cc

#include "datatypeB.h"
#include "generalmaker.h"

char nameB[] = "DataTypeB";
//template<> //If I uncomment this, g++ 3.4 compiles but runs incorrectly
const GeneralMakerTP<DataTypeB, nameB>
GeneralMakerTP<DataTypeB,nameB>::registerThis;
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se


Jul 22 '05 #3

P: n/a
On Mon, 22 Nov 2004 15:35:11 +0100, Erik Arner wrote:

It has something to
do with instantiation of static members in template classes, AFAIU.


OK, so I'm getting closer to the problem now. The problem is definitely
that the following lines in typeAmaker.cc (and correspondingly in
typeBmaker.cc) aren't parsed the way I expect them to:

char nameA[] = "DataTypeA";
template<>
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;

It seems that the compiler does not interpret this as an instantiation of
a static variable. I have no clue as to what the compiler makes of the
above statement. Is it ill-formed?

If I add empty parenthesis after registerThis (= a call to the default
constructor, right?) the compiler complains that there's no member
function of that name declared.

If I add a dummy constructor that takes an int to the GeneralMakerTP class
and call it instead, the program works correctly after this modified
statement:

char nameA[] = "DataTypeA";
template<>
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis(0);

This finally makes the compiler understand that I'm initalizing the
static member. I really want to avoid this kind of ugly hackery. Help much
appreciated. Any ideas?

Thanks,
Erik

Jul 22 '05 #4

P: n/a

OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};

// template<> A<int> A<int>::a();
/*
g++ 3.4.2: error: no member function `a' declared in `A<int>'
g++ 3.3.3: error: no member function `a' declared in `A<int>'
*/

// A<int> A<int>::a;
/*
g++ 3.4.2: error: too few template-parameter-lists
g++ 3.3.3: Compiles, output: A() called
*/

// template<> A<int> A<int>::a;
/*
g++ 3.4.2: Compiles, but no output
g++ 3.3.3: Compiles, but no output
*/

template<> A<int> A<int>::a(0);
/*
g++ 3.4.2: Compiles, output: A(int) called
g++ 3.3.3: Compiles, output: A(int) called
*/

int main()
{
return 0;
}
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #5

P: n/a
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};


Generic version:

template < typename T >
A< T > A< T >::a;

AFAICT you can't specialize a individual static member as
the syntax is needed for when the whole class is specialized:

template <> class A< int >
{
static A< double > a;
}

template <>
A< double > A< int >::a;

However no compiler I tried gave a sensible diagnostic when an
explicit specialization was provided *without* explicitly
specializing A< int >.

The best was Borland CBuilderX (EDG) that found A< int >::a was
an unresolved external for some reason. The others presumably
just ignored it.

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;

HTH.

*) Can't remember the terminoligy for this, as I've never
needed to do it, "explicitly" is just a guess.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #6

P: n/a
On 22 Nov 2004 19:53:10 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?

Thanks,
Erik

Code:

#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};


Generic version:

template < typename T >
A< T > A< T >::a;

AFAICT you can't specialize a individual static member as
the syntax is needed for when the whole class is specialized:

template <> class A< int >
{
static A< double > a;
}

template <>
A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;

Here's a full example:

template <class T>
struct A
{
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;

//whole class specialization for float, with template<>
template <>
struct A<float>
{
static A<float> a;
};

//definition of A<float>::a, no template<>
A<float> A<float>::a;

int main()
{
A<float>::a, A<double>::a, A<char>::a; //check defined
}

See 14.7.3/5.

Tom
Jul 22 '05 #7

P: n/a
Tom Widmer wrote in news:bl********************************@4ax.com in
comp.lang.c++:

That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;

See 14.7.3/5.


Thanks Tom, I've seen this before but managed to forget it.

However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;

//whole class specialization for float, with template<>
template <>
struct A<float>
{
A() { std::cout << "A<float>\n"; }
static A<float> a;
};

//definition of A<float>::a, no template<>
A<float> A<float>::a;

template < typename T > void exists( T & ) {}

int main()
{
exists( A<float>::a );
exists( A<double>::a );
exists( A<char>::a );
}

The only compiler I have that compiled it was MSVC7.1 and it gave:

A<float>
A< [char] >

All the others (gcc/g++ 3.3,3.4,4.0 and BCCX 6.0 (EDG)) gave:

ccwlaaaa.o(.text+0xc5):test.cpp: undefined reference to `A<double>::a'

Or similar.

So I hope the OP can live with the generic version, as static member
specialization doesn't seem to be available yet :-(.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #8

P: n/a

Thanks for the input! Comments below.

On Mon, 22 Nov 2004 19:53:10 +0000, Rob Williscroft wrote:
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?
#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }
A(int) { cerr<<"A(int) called"<<endl; }

private:
static A a;
};

Generic version:

template < typename T >
A< T > A< T >::a;


OK, this compiles but doesn't initialize the static member (of course).
[SNIP]

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;


Wow. I've never seen this syntax before. If I use it alone (without the
generic version above), g++ 3.4.2 (and 3.3.3) says "error: explicit
instantiation of `A<int>::a' but no definition available". If I add the
generic version before it, it compiles and runs correctly. See below for
complete code that now works.

The same goes for my more complex example program earlier in the thread
(see separate post).

My only problem right now is that I don't quite understand what's going
on. Specifically:

* Why does everything work as I expect it to when I instantiate the static
member using the constructor that takes a parameter but not with the
default constructor? What's the fundamental difference?

* Does this solution (see below for code) follow the standard or shall I
expect it to break the next time I upgrade the compiler?

Again, many thanks for the help.

Regards,
Erik

Output (as expected and desired):

bash$ g++ -o test test.cc
bash$ ./test
A() called
bash$

Code:

//file: test.cc
#include <iostream>

using namespace std;

template <class T>
class A
{
public:
A() { cerr<<"A() called"<<endl; }

private:
static A a;
};
template <class T> A<T> A<T>::a;//Generic definition
template A<int> A<int>::a;//Specific definition

int main()
{
return 0;
}

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #9

P: n/a
On Mon, 22 Nov 2004 15:35:11 +0100, Erik Arner wrote:

Could someone please help me understand what's going on here? I'd be very
grateful.


As stated elsewhere in the thread, I think I have the problem under
control. The solution is to add a generic definition of the static member
in generalmaker.h, and remove the "<>" from "template <>" statement in the
..cc files (typeAmaker.cc and typeBmaker.cc).

My understanding of what's going on is still very limited. First and
foremost I wonder if the code below is stable and standards compliant, and
will behave the same the next time I go to a higher gcc version. Any input
would be appreciated.

Thanks,
Erik
Successful run with g++ 3.4.2:

bash$ g++ -c main.cc
bash$ g++ -c generalmaker.cc
bash$ g++ -c typeAmaker.cc
bash$ g++ -c typeBmaker.cc
bash$ g++ -o makertest main.o generalmaker.o typeAmaker.o typeBmaker.o
bash$ ./makertest
DataTypeA
DataTypeB
bash$

Code:

-----8<-----
//main.cc

#include <iostream>
#include "generalmaker.h"
#include <string>
#include <list>

using namespace std;

int main() {

list<string> typelist = GeneralMaker::listRegistered();

for( list<string>::iterator it = typelist.begin(); it != typelist.end(); ++it ) {
cerr<<*it<<endl;
}

return 0;
}

-----8<-----
//generalmaker.h

#ifndef GENERALMAKER_H
#define GENERALMAKER_H

#include <map>
#include <string>
#include <list>

class GeneralData;

class GeneralMaker
{
public:
virtual ~GeneralMaker() {}
static GeneralData * newData( const std::string& );
static std::list<std::string> listRegistered();
protected:
GeneralMaker( ) {}
virtual GeneralData* makeData() = 0;
typedef std::map< std::string , GeneralMaker * > MakerMap;
static MakerMap & registry();

};

template <class Data, const char * str_>
class GeneralMakerTP : public GeneralMaker
{
protected:
GeneralMakerTP() : GeneralMaker( )
{
Data d;
std::string str = d.uniqueName();
GeneralMaker::registry().insert( make_pair( str, this ) );
}
GeneralData* makeData()
{
return new Data();
}
static const GeneralMakerTP<Data, str_> registerThis;
};

// Following two lines added, generic definition of static member
template <class Data, const char * str_>
const GeneralMakerTP<Data, str_> GeneralMakerTP<Data, str_>::registerThis;

#endif

-----8<-----
//generalmaker.cc

#include "generalmaker.h"
#include "generaldata.h"

using namespace std;

list<string> GeneralMaker::listRegistered()
{
list<string> list;

for ( MakerMap::const_iterator it = registry().begin(); it != registry().end(); ++it )
{
list.push_back( it->first );
}
return list;
}

GeneralMaker::MakerMap & GeneralMaker::registry()
{
/* We use this because static instances of sub classes of GeneralMaker
make use of this map. The idea comes from c++ faq lite
[10.12] How do I prevent the "static initialization order fiasco"?
*/

static GeneralMaker::MakerMap reg;
return reg;
}

GeneralData* GeneralMaker::newData( const std::string& generalDataType )
{
GeneralMaker* maker =
(*registry().find( generalDataType )).second;
return maker ? maker->makeData() : NULL;
}

-----8<-----
//generaldata.h

#ifndef GENERALDATA_H
#define GENERALDATA_H

#include <string>
#include "generalmaker.h"
#include "generaldata.h"
class GeneralData
{
public:
GeneralData() {}
virtual ~GeneralData(){}

virtual std::string uniqueName() = 0;

};

#endif

-----8<-----
//datatypeA.h

#ifndef DATATYPEA_H
#define DATATYPEA_H

#include "generaldata.h"
#include <string>

class DataTypeA : public GeneralData
{
public:
DataTypeA( ) : GeneralData() {}
~DataTypeA() {}
std::string uniqueName() { return std::string("DataTypeA"); }

};

#endif

-----8<-----
//datatypeB.h

#ifndef DATATYPEB_H
#define DATATYPEB_H

#include "generaldata.h"
#include <string>

class DataTypeB : public GeneralData
{
public:
DataTypeB( ) : GeneralData() {}
~DataTypeB() {}
std::string uniqueName() { return std::string("DataTypeB"); }

};

#endif

-----8<-----
//typeAmaker.cc

#include "datatypeA.h"
#include "generalmaker.h"

char nameA[] = "DataTypeA";
template
const GeneralMakerTP<DataTypeA, nameA> GeneralMakerTP<DataTypeA,nameA>::registerThis;
//N.B., no <> after template

-----8<-----
//typeBmaker.cc

#include "datatypeB.h"
#include "generalmaker.h"

char nameB[] = "DataTypeB";
template
const GeneralMakerTP<DataTypeB, nameB> GeneralMakerTP<DataTypeB,nameB>::registerThis;
//N.B., no <> after template
--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #10

P: n/a
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:
Generic version:

template < typename T >
A< T > A< T >::a;
OK, this compiles but doesn't initialize the static member (of
course).


static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.

But see Tom's correction to My post too.
[SNIP]

You can however explicity (*) instantiate a specific static
member in a TU:

template A< int > A< int >::a;
Wow. I've never seen this syntax before. If I use it alone (without
the generic version above), g++ 3.4.2 (and 3.3.3) says "error:
explicit instantiation of `A<int>::a' but no definition available". If
I add the generic version before it, it compiles and runs correctly.
See below for complete code that now works.

The same goes for my more complex example program earlier in the
thread (see separate post).

My only problem right now is that I don't quite understand what's
going on. Specifically:

* Why does everything work as I expect it to when I instantiate the
static member using the constructor that takes a parameter but not
with the default constructor? What's the fundamental difference?


Not sure what you mean here, but (see my reply to Tom) there aren't
AFAICT any compilers that get all of this static initialization /
specalization business right, so maybe its a compiler bug.

* Does this solution (see below for code) follow the standard or shall
I expect it to break the next time I upgrade the compiler?


It's standard conforming (AFAICT) so it should work now and in the
future, it's also much simpler than trying to specialize a static
member so it should work everywhere now.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #11

P: n/a
On 23 Nov 2004 11:17:17 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;


GCC bizarrely looks like treats that only as a declaration, and not a
definition. This fixed it for GCC:

template<>
A<double> A<double>::a = A<double>();

If I get como433 up and running again later, I'll give it a go and see
if it manages to compile the code.

Tom
Jul 22 '05 #12

P: n/a
On Tue, 23 Nov 2004 14:55:27 +0000, Rob Williscroft wrote:
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:
Generic version:

template < typename T >
A< T > A< T >::a;


OK, this compiles but doesn't initialize the static member (of
course).


static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.


But it doesn't produce any output until the next definition (see below) is
added, so I really don't understand what the statement actually does :-/
template A< int > A< int >::a;


This invokes the default constructor together with the generic version, it
seems. I still don't really grasp what's going on...

Thanks,
Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #13

P: n/a
On Tue, 23 Nov 2004 14:58:13 +0000, Tom Widmer wrote:
On 23 Nov 2004 11:17:17 GMT, Rob Williscroft <rt*@freenet.co.uk>
wrote:
However I modified your example:

#include <iostream>

template <class T>
struct A
{
A() { std::cout << "A< [" << typeid( T ).name() << "] >\n"; }
static A a;
};

//general definition
template <class T>
A<T> A<T>::a;

//specialization of a for double, with template<>
template<>
A<double> A<double>::a;


GCC bizarrely looks like treats that only as a declaration, and not a
definition. This fixed it for GCC:

template<>
A<double> A<double>::a = A<double>();


OK, so now the question becomes: which method is "better", i.e. which will
probably be more stable and not break with a more strict, future compiler?
Do both versions follow the standard? Both versions compile and work with
both g++ 3.3.3 and 3.4.2, and both versions compile without warnings with
the online version of Comeau 4.3.3 (www.comeaucomputing.com/tryitout).

Version A:

template <class T> A<T> A<T>::a;
template<> A<double> A<double>::a;

Version B:

template<> A<double> A<double>::a = A<double>();

Regards,
Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #14

P: n/a
Erik Arner wrote in news:pa****************************@hotmail.com in
comp.lang.c++:
static members without a initializer are default-initialized, in
this case that means A< T >::A() is invoked.


But it doesn't produce any output until the next definition (see
below) is added, so I really don't understand what the statement
actually does :-/
template A< int > A< int >::a;


This invokes the default constructor together with the generic
version, it seems. I still don't really grasp what's going on...


Ok I think I get it now, because A a class template its members
don't get implicitly instantiated until you try to use them.

In the case of A< int >::a its necessary to reference it in some
way, say by accessing a member or passing a refrence or pointer
to a function.

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #15

P: n/a
On Tue, 23 Nov 2004 15:24:34 +0000, Rob Williscroft wrote:

Ok I think I get it now, because A a class template its members
don't get implicitly instantiated until you try to use them.
Right.
In the case of A< int >::a its necessary to reference it in some
way, say by accessing a member or passing a refrence or pointer
to a function.

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.


But I still need the generic definition somewhere before this statement,
otherwise it won't compile.

/Erik

--
My Hotmail address is a spam magnet. If replying by email,
please use erik dot arner at cgb dot ki dot se

Jul 22 '05 #16

P: n/a
Erik Arner wrote in news:pa***************************@hotmail.com in
comp.lang.c++:

The:

template A< int > A< int >::a;

explicitly instantiates the member so it is no longer necessary
for the TU to reference it.


But I still need the generic definition somewhere before this statement,
otherwise it won't compile.


Yes, the above is an instantiation not a declaration, so the
declaration is still needed.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #17

P: n/a
Tom Widmer <to********@hotmail.com> wrote:
Rob Williscroft <rt*@freenet.co.uk> wrote:
Erik Arner wrote:
OK, I think my problem boils down to the following. How do I initialize
A::a using the default constructor in the code below?
template <class T>
struct A
{
static A a;
};

template <>
A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;


(Bear with me, this is all new to me..)
I don't understand this line. It means that A<int> has a static
member whose type is A<double> ?
Supposing we then had:

A<float> A<int>::a;

and then in a function somewhere:
A<int> b;
foo(b.a)
how do we know whether it is foo(A<float>) or foo(A<double>)
that gets called?
Jul 22 '05 #18

P: n/a
Old Wolf wrote in news:84**************************@posting.google.c om
in comp.lang.c++:
Tom Widmer <to********@hotmail.com> wrote:
Rob Williscroft <rt*@freenet.co.uk> wrote:
template <class T>
struct A
{
static A a;
};

>template <>
>A< double > A< int >::a;


That's incorrect - if the whole class is specialized, you shouldn't
use the template<> bit. That should be:

A<double> A<int>::a;


(Bear with me, this is all new to me..)
I don't understand this line. It means that A<int> has a static
member whose type is A<double> ?
Supposing we then had:

A<float> A<int>::a;


But you can't the specalization A< int > that's been snipped is:

template <> class A< int >
{
static A< double > a;
}

So A<int>::a has the declared type of A< double > trying to define
it as A< float > will be an error (illegal redeclaration or some
such).

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #19

This discussion thread is closed

Replies have been disabled for this discussion.