tags:

views:

230

answers:

3

See the following code and please clear my doubts.

  1. As ABC is a template, why does it not show an error when we put the definition of the ABC class member function in test.cpp?

  2. If I put test.cpp code in test.h and remve 2, then it works fine. Why?

.

// test.h 
template <typename T> 
class ABC { 
public: 
   void foo( T& ); 
   void bar( T& ); 
}; 
// test.cpp 
template <typename T> 
void ABC<T>::foo( T& ) {} // definition 
template <typename T> 
void ABC<T>::bar( T& ) {} // definition 

template void ABC<char>::foo( char & );  // 1 
template class ABC<char>;                // 2 
// main.cpp 
#include "test.h" 
int main() { 
   ABC<char> a; 
   a.foo();     // valid with 1 or 2 
   a.bar();     // link error if only 1, valid with 2 
} 
A: 

(This is an edited version of my original answer, prompted by David Rodriguez's observation.)

#1 instantiates the class template, and as part of that instantiates all its methods.

#2 instantiates one member method of the class. As part of that it has to instantiate the class template, but not all its other methods.

The difference can be seen if you introduce a type-dependent error into bar() (e.g. a statement like void *x = b;). You'll get a compiler error with #1 but not with #2. Also note that the compiler (gcc at least) would not compile #1 followed by #2, but would compile either of them without the other, or if #2 were followed by #1.

DS
When instantiating class templates, only members actually _used_ are instantiated. Which is why I believe you #2 is wrong. David's answer says so, too.
sbi
+10  A: 

In both cases you are doing an explicit instantiation. In the second case, only ABC<char>::foo is being instantiated, while in the first case ABC<char>::bar is also being instantiated.

A different similar example may clarify the implications:

// test.h
template <typename T>
class ABC {
public:
   void foo( T& );
   void bar( T& );
};
// test.cpp
template <typename T>
void ABC<T>::foo( T& ) {} // definition
template <typename T>
void ABC<T>::bar( T& ) {} // definition

template void ABC<char>::foo( char & );  // 1
template class ABC<char>;                // 2
// main.cpp
#include "test.h"
int main() {
   ABC<char> a;
   a.foo();     // valid with 1 or 2
   a.bar();     // link error if only 1, valid with 2
}

In the example, in main the compiler cannot see foo nor bar definitions, so it cannot instantiate the methods. The compiler, when processing main.cpp will accept the code in main gladly, since you are telling it that ABC is a template and that it has those two functions, and will assume that they will be defined in some other translation unit.

In the translation unit that contains test.cpp the compiler is seeing both method definitions, and the both of the instantiations (method/class) can be fully processed. If only the method instantiation ([1]) is present, the compiler will only generate that method, and will leave bar undefined. So any code that includes test.h, links against the compiled test.cpp and uses only foo method will compile and link, but usage of bar will fail to link due to it being undefined.

Explicitly instantiating the class template generates the symbols for all the member methods, and in that case, any translation unit that includes test.h and links against the compiled test.cpp object file will both compile and link.

David Rodríguez - dribeas
i have two doubts1. as ABC is template why it not showing error when we put defination of ABC class member function in test.cpp2.if i put test.cpp code in test.h and remove 2 then it working fine can u please explain
Template method definitions do not need to be inside the class body, even if it is recommended. As with regular functions, if the compiler only sees the method declaration it will assume that it is defined somewhere else, as in the example in the cpp. This approach is problematic in that you are limiting the types that can be used with your template to those for which you provide an explicit specialization. This can be used to speed up compile time for heavily templated code for which all the types that will instantiate the template are known in advance.
David Rodríguez - dribeas
On the second question, if you push all the code together, the compiler sees the template definition (method), so when you call the `bar` method within main it will perform an implicit instantiation of that method. Note that explicit instantiation is only required if the compiler cannot implicitly instantiate the template, and that in most cases if the compiler has enough information, by allowing it to decide what methods to instantiate (only those used) you will improve compile times (and intermediate object files, even if in most cases the linker can discard unused symbols)
David Rodríguez - dribeas
A: 

I guess you wanted to have {} instead of ; in #1.

Gabriel Ščerbák