another template specialization

  • Thread starter Tommi =?UTF-8?B?TcOka2l0YWxv?=
  • Start date
T

Tommi =?UTF-8?B?TcOka2l0YWxv?=

Hi,

I try to implement a factory for classes. There are 2 kinds of them, which
have different constructors. One of them is the base class. The factory
gets all needed parameters in his create-method. I would like to specialize
my factory template depending on the base-class. Here is a (not working)
example:

#include <iostream>

class Base
{
public:
Base()
{ std::cout << "Base::Base()" << std::endl; }
};

class Base2 : public Base
{
int b;
public:
Base2(int b_)
: b(b_)
{ std::cout << "Base2::Base2(" << b_ << ')' << std::endl; }
};

class Derived : public Base
{
public:
Derived()
{ std::cout << "Derived::Derived()" << std::endl; }
};

class Derived2 : public Base2
{
public:
Derived2(int d_) : Base2(d_)
{ std::cout << "Derived2::Derived2(" << d_ << ')' << std::endl; }
};

template <typename T>
class Factory
{
public:
Base* create(int a)
{ return new T(); }
};

template <>
class Factory<Base2>
{
public:
Base* create(int a)
{ return new Base2(a); }
};

int main()
{
Base* a;

Factory<Base> factoryA;
Factory<Base2> factoryB;
Factory<Derived> factoryC;
Factory<Derived2> factoryD;

a = factoryA.create(1); delete a;
a = factoryB.create(1); delete a;
a = factoryC.create(1); delete a;
a = factoryD.create(1); delete a;
}

As expected Factory<Derived2> does not use the specialization, because
Derived2 is not Base2, but only derived from Base2. Even if the compiler
would use it, it would call "new Base2(int)", but not "new Derived2(int)".
A possible solution is to put a second template-parameter here and pass the
class and its baseclass, but I'm feel, there is a better solution for this.

Tommi
 
V

Victor Bazarov

Tommi said:
I try to implement a factory for classes. There are 2 kinds of them,
which have different constructors. One of them is the base class. The
factory gets all needed parameters in his create-method. I would like
to specialize my factory template depending on the base-class. Here
is a (not working) example:

#include <iostream>

class Base
{
public:
Base()
{ std::cout << "Base::Base()" << std::endl; }
};

class Base2 : public Base
{
int b;
public:
Base2(int b_)
: b(b_)
{ std::cout << "Base2::Base2(" << b_ << ')' << std::endl; }
};

class Derived : public Base
{
public:
Derived()
{ std::cout << "Derived::Derived()" << std::endl; }
};

class Derived2 : public Base2
{
public:
Derived2(int d_) : Base2(d_)
{ std::cout << "Derived2::Derived2(" << d_ << ')' << std::endl; }
};

template <typename T>
class Factory
{
public:
Base* create(int a)

I really don't understand the reason for 'int a' to be here. You
are not using it. If you intend to have a polymorphic 'create'
member function, then declare it virtual. And then the other
Factory should just derive from this one, not be a specialisation.
Or both should derive from the base. Or just one -- the template.
Or simply make 'create' function a template.
{ return new T(); }
};

template <>
class Factory<Base2>
{
public:
Base* create(int a)
{ return new Base2(a); }
};

int main()
{
Base* a;

Factory<Base> factoryA;
Factory<Base2> factoryB;
Factory<Derived> factoryC;
Factory<Derived2> factoryD;

a = factoryA.create(1); delete a;

What is '1' doing there? Answer: nothing. So, why is it there? No
reason.
a = factoryB.create(1); delete a;
a = factoryC.create(1); delete a;

Same here. There is no reason for this argument.
a = factoryD.create(1); delete a;
}

As expected Factory<Derived2> does not use the specialization, because
Derived2 is not Base2, but only derived from Base2. Even if the
compiler would use it, it would call "new Base2(int)", but not "new
Derived2(int)". A possible solution is to put a second
template-parameter here and pass the class and its baseclass, but I'm
feel, there is a better solution for this.

Right now it is absolutely unclear *why* you're trying to do all that.
Why don't you simply define two factories (both templates), one for
constructing without an argument, the other for constructing with one
argument. What does "Modern C++ Design" recommend in that case? My
copy is in the office...

V
 
T

Tommi =?UTF-8?B?TcOka2l0YWxv?=

Hi,

the example is really just a example. I now this is absolutely useless. It
is just a reduced problem from a larger system.

I try to explain the problem:

when I declare a class, I want to be a able to delcare a factory with a
template. The syntax should be:

typedef Factory<MyClass> MyClassFactory;

Later I want to create a instance with:

MyClassFactory.create(some_argument);

If MyClass is derived from e.g. Base, the factory should create a
constructor call "new MyClass()" and ignore the arguments to my
create-method. If it is derived from e.g. Base2 it should use this argument
and call "new MyClass(some_argument)". So I want to create a specialization
from Factory for all classes derived from Base2.


Tommi
 
V

Victor Bazarov

Tommi said:
the example is really just a example. I now this is absolutely
useless. It is just a reduced problem from a larger system.

I try to explain the problem:

when I declare a class, I want to be a able to delcare a factory
with a template. The syntax should be:

typedef Factory<MyClass> MyClassFactory;

Later I want to create a instance with:

MyClassFactory.create(some_argument);

A typedef-id cannot be used as the left-hand side of the 'dot' operator.
Did you mean

MyClassFactory::create(some_argument);

?
If MyClass is derived from e.g. Base, the factory should create a
constructor call "new MyClass()" and ignore the arguments to my
create-method. If it is derived from e.g. Base2 it should use this
argument and call "new MyClass(some_argument)". So I want to create a
specialization from Factory for all classes derived from Base2.

Perhaps you want to look at Boost, and especially its 'is_derived'
template. I am not sure how it's going to help, to be honest, but
it's a template, so you could try to specialise depending on its
compile-time value (true/false).

It is still much easier to do

template<class T, class B> struct Factory {
static B* create() { return new T(); }
template<class P> static B* create(P p) { return new T(p); }
... and so on.
};


typedef Factory<MyClass,Base> MyClassFactory;

Base* pb = MyClassFactory::create();

typedef Factory<MyClass,Base2> MyClassFactory2;

Base2* pb = MyClassFactory2::create(42); // will use the template
// member with 1 arg

V
 

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,780
Messages
2,569,608
Members
45,242
Latest member
KendrickKo

Latest Threads

Top