13
votes

I figured an explicit instantiation request would automatically instantiate all base class members also, but I get a linker error: unresolved external symbol "public: void Base<int>::foo(int)" when building this code using Visual Studio 2008 or 2010.

Note that adding a call to foo() inside bar() forces the compiler to instantiate Base<int>::bar() and the build succeeds, so it appears that the compiler has all the necessary information to instantiate foo().

Obviously, explicitly instantiating Base<int> in source.cpp allows the build to succeed, but it seems silly to need to explicitly instantiate any dependent base classes whenever explicitly instantiating a derived class.

Is this normal? I couldn't find what the standard says regarding this issue.

header.h

template<typename T>
class Base {
public:
    void foo();
};

template<typename T>
class Derived : public Base<T> {
public:
    void bar();
};

source.cpp

#include "header.h"

template<typename T>
void Base<T>::foo() { }

template<typename T>
void Derived<T>::bar() {
    // this->foo();   // adding this forces instantiation of foo()???
}

template class Derived<int>;

main.cpp

#include "header.h"

int main() {
    Derived<int> d;
    d.foo(); // Linker Error: unresolved external symbol "public: void Base<int>::foo(int)"
}

Edit:

It looks like the Standard says only members of a class get instantiated by an explicit class instantiation, so the linker error is justified in my example.

Note that a class is defined by class-head { member-specification } and "The member-specification in a class definition declares the full set of members of the class; no member can be added elsewhere." So members are only between the curly braces { }, and public base class members don't become members of the derived class, they are merely accessible from the derived class or by objects of the derived class.

My only remaining question is why the Standard specifies that explicit instantiation of a class template only instantiates members and not members of base classes? My guess is that this allows greater control of what gets explicitly instantiated where. Someone that's using explicit template class instantiations would most likely have the base class definitions in a different file than the derived class definitions, and would explicitly instantiate each separately.

1
Does adding an explicit constructor for Derived trigger instantiation of the Base class? I suspect the compiler is trying not to do more work than is necessary. Having a constructor might make it realize that it needs to construct Base as well, and that might trigger it. Also, have you tried gcc or other compilers?Adrian McCarthy
Adding a constructor with or without an argument and with or without keyword "explicit" does not trigger instantiation of base class foo(), but a call to foo() from the derived member bar() triggers the implicit instantiation.JohnPS

1 Answers

9
votes

The Standard says

The explicit instantiation of a class template specialization implies the instantiation of all of its members not previously explicitly specialized in the translation unit containing the explicit instantiation.

In other words, it does not mandate that base classes are explicitly instantiated in turn. It will cause an implicit instantiation of them which will not instantiate their member definitions up-front. It's some ugly glitch in the Standard as to whether some text when it says "member" means "direct" or "inherited" member, as that often seems to be "obvious" to the one who wrote the Standards wording, but not to the one who reads it. C++0x has added some clarifications (it also has a difference between explicit instantiation declarations and definitions that C++03 doesn't have, but even ignoring that, the C++0x wording contains some more bits of insight):

An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below. [ Note: In addition, it will typically be an explicit instantiation of certain implementation-dependent data about the class. — end note ]