 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Stefan Rupp Guest
|
Posted: Sun Dec 14, 2003 8:51 pm Post subject: Prevent template member functions from being instantiated |
|
|
Good afternoon,
we have a problem with the instantiation of template member functions.
Consider the following piece of code:
-----------------------------------------------------------------
struct C {
virtual ~C();
};
struct PtrBase {
C* lookup();
#if defined(WITH_VIRTUAL_FOO)
virtual
#endif
bool foo(const PtrBase&);
};
template < class T >
struct Ptr : public PtrBase {
bool foo(const PtrBase&);
};
template < class T >
bool
Ptr<T>::foo(const PtrBase& to) {
if (lookup()) {
return dynamic_cast<T*>(to.lookup());
} else {
return (dynamic_cast<const Ptr(&to) != 0);
}
}
// class Model is derived from C
struct Model; // instead of #include it's declaration
Ptr<Model>
f(const Ptr<Model>& op)
{
return op;
}
-----------------------------------------------------------------
Compiling this with either g++ 2.95 or g++ 3.3 works fine unless the
symbol WITH_VIRTUAL_FOO is defined. If it is, we get the error message
foo.cc: In method `bool Ptr<Model>::foo(const PtrBase &)':
foo.cc:37: instantiated from here
foo.cc:24: passing `const PtrBase' as `this' argument of `struct C *
PtrBase::lookup()' discards qualifiers
foo.cc:24: cannot dynamic_cast `(+to)->PtrBase::lookup()' (of type
`struct C *') to type `struct Model *'
As far as I can say, the member function foo of the class template
Ptr<T> is instantiated although it is not necessary. The problem goes
away if foo isn't virtual or if we #include the declaration of class
Model rather than declaring it forward.
We really would like to make function foo virtual without the need to
#include all declarations of the involved classes.
Is there a standard way of preventing an implementation to instantiate a
virtual function in the above context?
Regards,
Stefan
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Dietmar Kuehl Guest
|
Posted: Mon Dec 15, 2003 1:38 pm Post subject: Re: Prevent template member functions from being instantiate |
|
|
Stefan Rupp <st.rupp (AT) t-online (DOT) de> wrote:
| Quote: | struct PtrBase {
C* lookup();
|
The above member function is a non-const member...
| Quote: |
#if defined(WITH_VIRTUAL_FOO)
virtual
#endif
bool foo(const PtrBase&);
};
[...]
template < class T
bool
Ptr
if (lookup()) {
return dynamic_cast<T*>(to.lookup());
|
.... but it is used on the 'const' object 'to'. You should probably correct
this error or remove the function alltogether because as it is is never
correct. It is just not checked for errors if it isn't used (and "use"
includes being virtual).
| Quote: | } else {
return (dynamic_cast<const Ptr(&to) != 0);
}
}
|
Actually, the error message already plainly pointed at the exact problem.
--
<mailto:dietmar_kuehl (AT) yahoo (DOT) com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Stefan Rupp Guest
|
Posted: Mon Dec 15, 2003 10:04 pm Post subject: Re: Prevent template member functions from being instantiate |
|
|
Good evening,
Dietmar Kuehl schrieb:
| Quote: | Stefan Rupp <st.rupp (AT) t-online (DOT) de> wrote:
struct PtrBase {
C* lookup();
The above member function is a non-const member...
#if defined(WITH_VIRTUAL_FOO)
virtual
#endif
bool foo(const PtrBase&);
};
[...]
template < class T
bool
Ptr
if (lookup()) {
return dynamic_cast<T*>(to.lookup());
... but it is used on the 'const' object 'to'. You should probably correct
this error or remove the function alltogether because as it is is never
correct. It is just not checked for errors if it isn't used (and "use"
includes being virtual).
} else {
return (dynamic_cast<const Ptr(&to) != 0);
}
}
Actually, the error message already plainly pointed at the exact problem.
|
You are absolutely right. It appears I had a problem minimising my code
to present it here. But: if we make both lookup() and foo() const
functions, the first message
foo.cc: In method `bool Ptr<Model>::foo(const PtrBase &)':
foo.cc:37: instantiated from here
foo.cc:24: passing `const PtrBase' as `this' argument of `struct C *
PtrBase::lookup()' discards qualifiers
disappears, but the second
foo.cc: In method `bool Ptr<Model>::foo(const PtrBase &) const':
foo.cc:41: instantiated from here
foo.cc:24: cannot dynamic_cast `(+to)->PtrBase::lookup()' (of type
`struct C *') to type `struct Model *'
remains. Here we have the instantiation problem in the first of the two
cast expressions in function foo():
template < class T >
bool
Ptr<T>::foo(const PtrBase& to) {
if (lookup()) {
return dynamic_cast<T*>(to.lookup());
} else {
return (dynamic_cast<const Ptr(&to) != 0);
}
}
It's not a const problem; the error goes away when #including the full
declaration of struct Model instead of forward-declaring it.
Am I making any sense here? I hope I didn't make another mistake.
Regards,
Stefan Rupp
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
tom_usenet Guest
|
Posted: Tue Dec 16, 2003 6:15 pm Post subject: Re: Prevent template member functions from being instantiate |
|
|
On 14 Dec 2003 15:51:11 -0500, Stefan Rupp <st.rupp (AT) t-online (DOT) de>
wrote:
| Quote: | Good afternoon,
we have a problem with the instantiation of template member functions.
Consider the following piece of code:
-----------------------------------------------------------------
struct C {
virtual ~C();
};
struct PtrBase {
C* lookup();
|
Surely
C* lookup() const;
| Quote: |
#if defined(WITH_VIRTUAL_FOO)
virtual
#endif
bool foo(const PtrBase&);
};
template < class T
struct Ptr : public PtrBase {
bool foo(const PtrBase&);
};
|
If foo is virtual, instantiations of Ptr
of Ptr<T>::foo (in order to build foo into the vtable for Ptr<T>).
| Quote: | template < class T
bool
Ptr
if (lookup()) {
return dynamic_cast<T*>(to.lookup());
} else {
return (dynamic_cast<const Ptr(&to) != 0);
}
|
The above requires that T be a dynamic type (with at least one virtual
function). It also requires that T be completely defined at the point
of instantiation.
| Quote: | }
// class Model is derived from C
struct Model; // instead of #include it's declaration
|
Incomplete definition.
| Quote: |
Ptr<Model
f(const Ptr
{
return op;
|
Instantiation of Ptr<Model>. Error since Model isn't complete, yet
your implementation of Ptr<T> requires completeness at all points of
instantiation of Ptr.
| Quote: | }
-----------------------------------------------------------------
Compiling this with either g++ 2.95 or g++ 3.3 works fine unless the
symbol WITH_VIRTUAL_FOO is defined. If it is, we get the error message
foo.cc: In method `bool Ptr<Model>::foo(const PtrBase &)':
foo.cc:37: instantiated from here
foo.cc:24: passing `const PtrBase' as `this' argument of `struct C *
PtrBase::lookup()' discards qualifiers
foo.cc:24: cannot dynamic_cast `(+to)->PtrBase::lookup()' (of type
`struct C *') to type `struct Model *'
As far as I can say, the member function foo of the class template
Ptr<T> is instantiated although it is not necessary.
|
virtual functions are always instantiated - how is the compiler to
know whether a particular virtual function in a derived class is
called or not? How is it to build a vtable for the class without
instantiating the function?
The problem goes
| Quote: | away if foo isn't virtual or if we #include the declaration of class
Model rather than declaring it forward.
We really would like to make function foo virtual without the need to
#include all declarations of the involved classes.
Is there a standard way of preventing an implementation to instantiate a
virtual function in the above context?
|
Nope. There are ways around the problem though - the obvious one is to
use function pointers to move the requirement that T be a complete
type to the constructor of Ptr. This is the standard trick used to
allow smart pointers that work with incomplete types - they hold a
function pointer to a destruction function. e.g.
struct C {
virtual ~C(){}
};
struct PtrBase {
private:
typedef bool (*fooptr_t)(PtrBase& from, const PtrBase& to);
fooptr_t fooptr;
public:
PtrBase(fooptr_t ptr)
:fooptr(ptr)
{
}
C* lookup() const {
return 0;
}
bool foo(const PtrBase& to)
{
fooptr(*this, to);
}
};
template <class T>
struct Ptr : public PtrBase
{
Ptr();
};
namespace impl
{
template <class T>
bool foofunc(PtrBase& from, const PtrBase& to)
{
if (from.lookup())
{
return dynamic_cast<T*>(to.lookup());
}
else
{
return (dynamic_cast<const Ptr(&to) != 0);
}
}
}
template <class T>
Ptr<T>::Ptr()
:PtrBase(&impl::foofunc<T>)
{
}
// class Model is derived from C
struct Model;
//fine since default constructor of Ptr<Model> isn't used:
Ptr<Model> f(const Ptr<Model>& op)
{
return op;
}
Tom
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|