2

I understood the concept of templates and why do we need to define the template member functions in header file. And another option is where define template functions in cpp file and explicitly instantiate template classes as below.

template.h

#include <iostream>

using namespace std;

template <typename T> class myclass
{
    public:
        void doSomeThing();
};

template.cpp

#include <iostream>
#include "template.h"

using namespace std;

template <typename T> void myclass<T>::doSomeThing()
{
    cout << "in DoSomething" << endl;
}

template class myclass <int>; // Why we shouldn't use template<> class myclass <int> here?

main.cpp

#include <iostream>
#include "template.h"

using namespace std;

int main()
{
    myclass<int> obj;
    obj.doSomeThing();
}

I am compiling on Ubuntu OS using g++ main.cpp template.cpp and I am able to call doSomeThing()

I have couple of questions as below.

  1. If we need to explicitly instantiate class template, it should be template <> class myclass <int> But when I use this in template.cpp instead of template class myclass <int>, it is throwing undefined reference to 'myclass::doSomeThing()' error. Why we shouldn't use <> in this case?
  2. I tried to instantiate an object for myclass (for int) as myclass <int> obj; instead of template <> class myclass <int>; in template.cpp as below.

    #include <iostream>
    #include "template.h"
    
    using namespace std;
    
    template <typename T> void myclass<T>::doSomeThing()
    {
        cout << "in DoSomething" << endl;
    }
    
    myclass <int> obj;
    

    I thought template.cpp has both template declaration through template.h and all the template function definitions in template.cpp so creating an object for int type will create the class for int and it will contain the function definition for int type. So when g++ compiles main.cpp, it has all the functions for int type and creation of object for myclass (for int data type) will work if I compile template.cpp first and main.cpp after as g++ template.cpp main.cpp. But this is also throwing main.cpp:(.text+0x1f): undefined reference to `myclass::doSomeThing()' error. I am unable to understand why this is throwing error. Can anyone please help me to understand why this is not working.

kadina
  • 5,042
  • 4
  • 42
  • 83
  • *If we need to explicitly instantiate class template, it should be `template <> class myclass `* Why? Who told you that? – NathanOliver Nov 06 '19 at 16:26

2 Answers2

4
  1. If we need to explicitly instantiate class template, it should be template <> class myclass <int>

No, it should not. That is not syntax for explicit instantiation. (It is syntax for template specialisation.)

Why we shouldn't use <> in this case?

Because that is not syntax for explicit instantiation. The correct syntax is:

template class|struct template-name < argument-list > ;

  1. am unable to understand why this is throwing error.

Because there is no explicit instantiation definition for the template. Without explicit instantiation, a template may not be used in TU where the template has not been defined.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • For 2, since the OP has `myclass obj;` in the source file, doesn't the compiler implicitly instantiate the template for `int`? – NathanOliver Nov 06 '19 at 16:34
  • @NathanOliver-ReinstateMonica Yes it does. But it does not explicitly instantiate it. – eerorika Nov 06 '19 at 16:34
  • I believe it is also worth mentioning that member functions (if not deleted) are only **declared**, rather than defined, when the class template is instantiated. – ph3rin Nov 06 '19 at 16:36
3

Your first question was why writing:

template<> class myclass <int>;

causes an undefined reference error. The answer is that template<> declares an explicit specialization: it's saying that there is a special definition of myclass<int>::doSomething that takes the place of the generic definition earlier in the file. However, the definition of the specialization is not provided. Therefore, no definition of myclass<int>::doSomething is emitted when template.cpp is translated.

Your second question was why replacing the explicit instantiation with:

myclass <int> obj;

in template.cpp did not cause myClass<int>::doSomething to be emitted from that translation unit. The answer is twofold:

  1. When the compiler implicitly instantiates myclass<int> when translating template.cpp, it does not implicitly instantiate myclass<int>::doSomething because, at that point, the function's definition is not yet needed.

  2. Even if the compiler were to implicitly instantiate myclass<int>::doSomething during the translation of template.cpp, that implicit instantiation is NOT guaranteed to make that instantiated definition available to other translation units. (The compiler may, in effect, generate it with internal linkage.)

See C++17 [temp]/7

A function template, member function of a class template, variable template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated (17.7.1) unless the corresponding specialization is explicitly instantiated (17.7.2) in some translation unit; no diagnostic is required.

What this is saying is that if you need to call a function template from translation unit 1, but you only want to define that function template in translation unit 2, then translation unit 2 must explicitly instantiate the function template with the desired arguments. An implicit instantiation doesn't count.

(Note that the explicit instantiation of myclass<int> will also implicitly instantiate the definitions of all of myclass<int>'s member functions.)

Brian Bi
  • 111,498
  • 10
  • 176
  • 312