[templates] is this code legal?

M

Marco Jez

Is it legal, in a class template's member function, to instantiate the same
class template but with a different type as argument? The following code is
very simple and I'd expect it to compile, but it makes both MSVC and gcc
freeze and never return. Sometimes I get an "internal compiler error"
instead.

struct Instance_base
{
virtual ~Instance_base() {}
virtual Instance_base* mkptr() = 0;
};

template<typename T>
struct Instance: Instance_base
{
T _inst;
Instance(const T &v): _inst(v) {}
Instance_base* mkptr() { return new Instance<T*>(&_inst); }
};

Instance_base* inst = new Instance<int>(3);


The offending line is that of Instance::mkptr(), if I remove it (and I make
the base class' method non-abstract) everything compiles ifne. Any clues?

Cheers,
Marco
 
A

Alf P. Steinbach

* Marco Jez:
Is it legal, in a class template's member function, to instantiate the same
class template but with a different type as argument?
Yes.


The following code is
very simple and I'd expect it to compile, but it makes both MSVC and gcc
freeze and never return. Sometimes I get an "internal compiler error"
instead.

struct Instance_base
{
virtual ~Instance_base() {}
virtual Instance_base* mkptr() = 0;
};

template<typename T>
struct Instance: Instance_base
{
T _inst;
Instance(const T &v): _inst(v) {}
Instance_base* mkptr() { return new Instance<T*>(&_inst); }
};

Instance_base* inst = new Instance<int>(3);


The offending line is that of Instance::mkptr(), if I remove it (and I make
the base class' method non-abstract) everything compiles ifne. Any clues?

You have an infinitely recursive template, just the same as e.g.

template< typename T >
int foo(){ return foo<T*>(); }

Amazingly MSVC is able to issue an error for this definition in almost no
time, "... too complex", but it insists that that is an error in <ios>...

The interesting question, too me, is whether 'mkptr' should really be
recursively instantiated, because it's never actually used.

So although the immediate problem fix is very clear, namely don't recurse
infinitely, and probably you _meant_ to write something like

Instance_base* mkptr() { return new Instance(_inst); }

the question you ask is not as clear, and I don't know the answer; however I
expect that someone will provide the answer, because it's nothing complicated,
just a matter of knowing or using the time to find the technical rule.

Btw., it's not a good idea to use an underscore at the start of a name.

Names starting with underscore followed by uppercase are reserved for the
implementation, and in practice one should regard that rule as the more
general that names staring with underscore are reserved.
 
P

puzzlecracker

try this:

struct Instance_base
{
virtual ~Instance_base() {}
virtual Instance_base* mkptr() = 0;



};


template<typename T>
struct Instance: Instance_base
{
T _inst;
Instance(const T &v): _inst(v) {}
Instance_base* mkptr() { return new Instance(_inst); }


};
 
P

puzzlecracker

You have an infinitely recursive template, just the same as e.g.

template< typename T >
int foo(){ return foo<T*>(); }
Why is it the same as above? Now, I am confused.
 
P

puzzlecracker

I thought that mkptr function wasn't generated at all (until it is
called for the first time). Is case that we have a definition of pure
virtual class that causes that function to be generated? (still don't
the infinite recursion - need to hone my metaprogramming skills :) ).


interesting question - I second it.
 
P

puzzlecracker

I see it now.

Here is an explanation (which I did already mention in passing):

Since we have an abstract class, any class that inherits from it, is
required to let the compiler know that it has overridden all of its
pure virtual functions (failing to do so is a compiler error), in which
case it starts looking for one instantiating each member function of a
template class until *Perfect* match is found. Otherwise, the subclass
is abstract by definition, and that would lead to another error.


cracker
 
M

Marco Jez

So although the immediate problem fix is very clear, namely don't recurse
infinitely, and probably you _meant_ to write something like

Instance_base* mkptr() { return new Instance(_inst); }

This is not what I need. These classes are part of a larger class whose
purpose is to hold any kind of value, along the lines of boost::any. Such
class is roughly defined as follows (other constructors and methods have
been intentionally omitted for clarity):

class object
{
public:
template<typename T>
object(const T &v): _inst(new instance<T>(v)) {}

object make_pointer()
{
object newobj;
newobj._inst = _inst->mkptr();
return newobj;
}

protected:
struct instance_base { .... };
template<typename T> struct instance: instance_base { ..... };

private:
instance_base* _inst;
};

Note that struct "instance" has a data member of type T and it has a
constructor that takes a value of type const T& and initializes the data
member with that value.

instance::mkptr() is used polymorphically by object::make_pointer(). The
purpose is to create a new instance of class object that contains a pointer
to the value held by the calling instance. For example:

// initializes object::_inst with an
// instance<int> containing the number 5
object x = 5;

// p should hold an instance<int*> containing
// a pointer to the value held by x
object p = x.make_pointer();

I don't know how to achieve that other than by providing that mkptr()
function.
Btw., it's not a good idea to use an underscore at the start of a name.

Yes I know that, but unfortunately this code is part of a larger project of
which I haven't control, and I was asked to change my style to match that of
the project. There's nothing more frustrating than being forced to write
programs as if you were someone else.

Marco
 
M

Marco Jez

Sorry for using lowercase identifiers in my last reply. I had to change
naming conventions so many times that I can't even focus well on the current
one, especially after a night spent on debugging :)

Marco
 
A

Alf P. Steinbach

* Marco Jez:
This is not what I need. These classes are part of a larger class whose
purpose is to hold any kind of value, along the lines of boost::any. Such
class is roughly defined as follows (other constructors and methods have
been intentionally omitted for clarity):

class object
{
public:
template<typename T>
object(const T &v): _inst(new instance<T>(v)) {}

object make_pointer()
{
object newobj;
newobj._inst = _inst->mkptr();
return newobj;
}

protected:
struct instance_base { .... };
template<typename T> struct instance: instance_base { ..... };

private:
instance_base* _inst;
};

Note that struct "instance" has a data member of type T and it has a
constructor that takes a value of type const T& and initializes the data
member with that value.

instance::mkptr() is used polymorphically by object::make_pointer(). The
purpose is to create a new instance of class object that contains a pointer
to the value held by the calling instance. For example:

// initializes object::_inst with an
// instance<int> containing the number 5
object x = 5;

// p should hold an instance<int*> containing
// a pointer to the value held by x
object p = x.make_pointer();

I don't know how to achieve that other than by providing that mkptr()
function.

It's unclear what you're trying to achieve, and I think that _is_ the problem.

Consider that with the current design you can end up with a pointer to a
pointer to a pointer ... to a pointer to something.

Perhaps try instead to have one value class and one value pointer class, both
derived from instance_base. But whether that helps depends on the
requirements of your design. I'd start by fleshing out the requirements more
precisely -- e.g., surely you don't mean to have 'object' assignable with
all those raw pointers bandied about? And perhaps if you think of higher
level requirements you can get rid of 'object' entirely. Such classes, or use
of boost::any or similar, is nearly always an indication of a flawed design.
 
M

Marco Jez

Such classes, or use
of boost::any or similar, is nearly always an indication of a flawed
design.

Only if you use them to "bypass" the strong-typed nature of C++, which is
something that many people who are unfamiliar with C++ try to do. This is
not my case as I'm writing an introspection library.

Marco
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top