Likely causes of Unresolved external symbol in pure virtual function

D

Dilip

Hi All

I have a pure virtual function in my base class that my derived
instances override. What are the likely causes for VC++ 7.1 to
complain of unresolved external symbol on that pure virtual function?
I tried to rule out the obvious mistakes. The virtual fn. is
definitely defined in the derived classes. Here is what I have:

// file a.h
class base
{
public:
virtual long somefunc() = 0;
};

template<typename T>
class derv : public base
{
virtual long somefunc();
};

// file a.cpp
template<typename T>
long derv<T>::somefunc()
{
}

I can't understand why I am getting the linker error -- what is my
mistake?
Earlier I had all these code in a single .cpp file (for testing
purposes), then I started re-organizing my project separating code into
headers and newer source files and all of a sudden the pure virtual
functions are giving me linker errors.

I have also ensured that the .cpp files of my derived classes that
define the pure virtual function generate proper .obj files.

What am i missing?
 
P

Phlip

Dilip said:
// file a.cpp
template<typename T>
long derv<T>::somefunc()
{
}

I can't understand why I am getting the linker error -- what is my
mistake?

Each translation unit must (generally) see complete template definitions to
expand them.

Move the body of somefunc() from a.cpp to a new file, call it a.inl, and
then #include this into every translation unit that instantiates your
template derv<> with a new type.
 
V

Victor Bazarov

Dilip said:
I have a pure virtual function in my base class that my derived
instances override. What are the likely causes for VC++ 7.1 to
complain of unresolved external symbol on that pure virtual function?
I tried to rule out the obvious mistakes. The virtual fn. is
definitely defined in the derived classes. Here is what I have:

// file a.h
class base
{
public:
virtual long somefunc() = 0;
};

template<typename T>
class derv : public base
{
virtual long somefunc();
};

// file a.cpp
template<typename T>
long derv<T>::somefunc()
{
}

I can't understand why I am getting the linker error -- what is my
mistake?

Isn't this in the FAQ? See section on templates.

V
 
D

Dilip

Phlip said:
Each translation unit must (generally) see complete template definitions to
expand them.

That should teach me a lesson -- mucking around with templates without
understanding everything about them.
Move the body of somefunc() from a.cpp to a new file, call it a.inl, and
then #include this into every translation unit that instantiates your
template derv<> with a new type.

would this be appropriate:
================================
// file derv1.inl
template<typename T>
long derv<T>::somefunc()
{
}
================================
================================
// file a.h
class base
{
virtual void somefunc() = 0;
};

template<typename T>
class derv1 : public base
{
virtual void somefunc();
};

// include this at the very bottom of a.h
// likewise include the defn of all classes that might derive from base
#include ".\derv1.inl"
================================
================================
// file a.cpp
#include ".\a.h"
================================

will this work?
 
P

Phlip

Dilip said:
That should teach me a lesson -- mucking around with templates without
understanding everything about them.

Note I said "generally". That's because I, too, muck around with templates
without understanding everything about them.

Modern compilers are supposed to overcome this limitation, but I don't know
how the new system works, or how many compilers support it. So almost all
projects use their templates the way we are now discussing...
would this be appropriate:
================================
// file derv1.inl
template<typename T>
long derv<T>::somefunc()
{
}
================================
================================
// file a.h
class base
{
virtual void somefunc() = 0;
};

template<typename T>
class derv1 : public base
{
virtual void somefunc();
};

// include this at the very bottom of a.h
// likewise include the defn of all classes that might derive from base
#include ".\derv1.inl"
================================

No, because that defeats the purpose of an INL file. Users of a.h might want
to know about the template's declaration without needing to know its
definition. Your system might as well put the template function body inside
a.h. There's nothing special about the .inl extension.

Only include a.inl in every .cpp file that needs it, and this should be
fewer files than need a.h. Generally.
 
D

Dilip

Victor said:
Isn't this in the FAQ? See section on templates.

Victor
Thanks for the pointer. I looked up the FAQ and read this (and others
too):
http://www.parashift.com/c++-faq-lite/templates.html#faq-35.15

While I understood the motivation, Philp's idea seems to be easier
because I am getting confused with 2 issues here.

#1) Cline's faq basically asks you to do a empty instantiation in the
..cpp file. So if my main code creates several different instances of
the derived template class with different template parameters, should I
include a empty instantiation for every such parameter?

#2) I had a situation like this that I couldn't immediately figure out
what to do:

// file a.h
class base
{
virtual void dosomething() = 0;

// I need the type parameter passed to derv for _only_
// this method in base
template<typename T>
void somefunc();
};

template<typename T>
class derv1 : public base
{
virtual void dosomething();
};

// derv1.cpp
template<typename T>
void derv<T>::dosomething()
{
somefunc<T>();
}

// file main.cpp
base* pBase = new derv1<int>();
pBase->dosomething();

how should I now define the empty instantiation for somefunc()?

is it:

template class base::somefunc<???>;

2 observations:

1. The above does not even compile (obviously the real code doesnt have
???)
2. the type parameter is dependant on the derived classes -- each of
them pass in their own stuff. How do I forward declare it?
 
V

Victor Bazarov

Dilip said:
Victor
Thanks for the pointer. I looked up the FAQ and read this (and others
too):
http://www.parashift.com/c++-faq-lite/templates.html#faq-35.15

While I understood the motivation, Philp's idea seems to be easier
because I am getting confused with 2 issues here.

#1) Cline's faq basically asks you to do a empty instantiation in the
.cpp file. So if my main code creates several different instances of
the derived template class with different template parameters, should
I include a empty instantiation for every such parameter?

There is no such thing as "empty instantiation". You probably mean
an explicit instantiation. It can help, but it's not the best solution.
An explicit instantiation causes the compiler to generate code for some
combination of the template arguments. If some user of your template
wants to have a different combination of template arguments than any of
those that you predicted, the user's compiler won't be able to generate
the instantiation if it does not see the code for the template itself.
#2) I had a situation like this that I couldn't immediately figure out
what to do:

// file a.h
class base
{
virtual void dosomething() = 0;

// I need the type parameter passed to derv for _only_
// this method in base
template<typename T>
void somefunc();
};

template<typename T>
class derv1 : public base
{
virtual void dosomething();
};

// derv1.cpp
template<typename T>
void derv<T>::dosomething()
{
somefunc<T>();
}

// file main.cpp
base* pBase = new derv1<int>();
pBase->dosomething();

how should I now define the empty instantiation for somefunc()?

is it:

template class base::somefunc<???>;

I think you'd need to put

template void derv<int>::dosomething();

in the 'derv1.cpp'.
2 observations:

1. The above does not even compile (obviously the real code doesnt
have ???)
2. the type parameter is dependant on the derived classes -- each of
them pass in their own stuff. How do I forward declare it?

Forward declare what? You don't need to do anything special, since
you have the template definition (which contains the declaration of
'dosomething').

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,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top