Consider this synthetic example. I have two native C++ projects in my Visual Studio 2010 solution. One is console exe and another is lib.
There are two files in lib:
// TImage.h
template<class T> class TImage
{
public:
TImage()
{
#ifndef _LIB
std::cout << "Created (main), ";
#else
std::cout << "Created (lib), ";
#endif
std::cout << sizeof(TImage<T>) << std::endl;
}
#ifdef _LIB
T c[10];
#endif
};
void CreateImageChar();
void CreateImageInt();
and
// TImage.cpp
void CreateImageChar()
{
TImage<char> image;
std::cout << sizeof(TImage<char>) << std::endl;
}
void CreateImageInt()
{
TImage<int> image;
std::cout << sizeof(TImage<int>) << std::endl;
}
And one file in exe:
// main.cpp
int _tmain(int argc, _TCHAR* argv[])
{
TImage<char> image;
std::cout << sizeof(TImage<char>) << std::endl;
CreateImageChar();
CreateImageInt();
return 0;
}
I know, I shouldn't actually do like this, but this is just for understanding what is happening. And that's, what happens:
// cout:
Created (main), 1
1
Created (main), 1
10
Created (lib), 40
40
So how exactly this happened, that linker overrides lib's version of TImage<char>
with exe's version of TImage<char>
? But since there is no exe's version of TImage<int>
, it preserves lib's version of TImage<int>
?.. Is this behavior standardized, and if so, where can I found the description?
Update: Explanations of the effect given below are correct, thanks. But the question was "how exactly this happened"?.. I expected to get some linker error like "multiply defined symbols". So the most suitable answer is from Antonio Pérez's reply.
Template code creates duplicated object code.
The compiler copies the template code for the type you provide when you instance the template. So when TImage.cpp
is compiled you get object code for two versions of your template, one for char and one for int in TImage.o
. Then main.cpp
is compiled and you get a new version of your template for char in main.o
. Then the linker happens to use the one in main.o
always.
This explains why your output yields the 'Created' lines. But it was a little bit puzzling to see the mismatch in lines 3, 4 regarding object's size:
Created (main), 1
10
This is due to the compiler resolving the sizeof
operator during compile-time.