Reputation: 7535
I have a template class like below.
template<int S> class A
{
private:
char string[S];
public:
A()
{
for(int i =0; i<S; i++)
{
.
.
}
}
int MaxLength()
{
return S;
}
};
If i instantiate the above class with different values of S, will the compiler create different instances of A() and MaxLenth() function? Or will it create one instance and pass the S as some sort of argument?
How will it behave if i move the definition of A and Maxlength to a different cpp file.
Upvotes: 0
Views: 1609
Reputation: 7434
Actually this is fully up to the compiler. It's only required to generate correct code for its inputs. In order to do so it must follow the C++ standard as that explains what is correct. In this case it says that the compiler must at one step in the process instantiate templates with different arguments as different types, these types may later be represented by the same code, or not, it's fully up to the compiler.
It's most probable that the compiler would inline at least MaxLength() but possibly also your ctor. Otherwise it may very well generate a single instance of your ctor and pass/have it retrieve S from elsewhere. The only way to know for sure is to examine the output of the compiler.
So in order to know for sure I decided to list what VS2005 does in a release build. The file I have compiled looks like this:
template <int S>
class A
{
char s_[S];
public:
A()
{
for(int i = 0; i < S; ++i)
{
s_[i] = 'A';
}
}
int MaxLength() const
{
return S;
}
};
extern void useA(A<5> &a, int n); // to fool the optimizer
extern void useA(A<25> &a, int n);
void test()
{
A<5> a5;
useA(a5, a5.MaxLength());
A<25> a25;
useA(a25, a25.MaxLength());
}
The assembler output is the following:
?test@@YAXXZ PROC ; test, COMDAT
[snip]
; 25 : A<5> a5;
mov eax, 1094795585 ; 41414141H
mov DWORD PTR _a5$[esp+40], eax
mov BYTE PTR _a5$[esp+44], al
; 26 : useA(a5, a5.MaxLength());
lea eax, DWORD PTR _a5$[esp+40]
push 5
push eax
call ?useA@@YAXAAV?$A@$04@@H@Z ; useA
As you can see both the ctor and the call to MaxLength() are inlined. And as you may now guess it does the same with the A<25> type:
; 28 : A<25> a25;
mov eax, 1094795585 ; 41414141H
; 29 : useA(a25, a25.MaxLength());
lea ecx, DWORD PTR _a25$[esp+48]
push 25 ; 00000019H
push ecx
mov DWORD PTR _a25$[esp+56], eax
mov DWORD PTR _a25$[esp+60], eax
mov DWORD PTR _a25$[esp+64], eax
mov DWORD PTR _a25$[esp+68], eax
mov DWORD PTR _a25$[esp+72], eax
mov DWORD PTR _a25$[esp+76], eax
mov BYTE PTR _a25$[esp+80], al
call ?useA@@YAXAAV?$A@$0BJ@@@H@Z ; useA
It's very interesting to see the clever ways the compiler optimizes the for-loop. For all those premature optimizers out there using memset(), I would say fool on you.
How will it behave if i move the definition of A and Maxlength to a different cpp file.
It will probably not compile (unless you only use A in that cpp-file).
Upvotes: 5
Reputation: 20724
Wherever S is used, a separate version of that function will get compiled into your code for each different S you instantiate.
Upvotes: 1
Reputation: 507313
If i instantiate the above class with different values of S, will the compiler create different instances of A() and MaxLenth() function? Or will it create one instance and pass the S as some sort of argument?
The compiler will instantiate a different copy of the class template for each different value of the parameter. Regarding the member functions, it will instantiate a different copy of each, for each different value of S
. But unlike member functions of non-template classes, they will only be generated if they are actually used.
How will it behave if i move the definition of A and Maxlength to a different cpp file.
You mean if you put the definition of A
into a header file, but define the member function MaxLength
in a cpp file? Well, if users of your class template want to call MaxLength
, the compiler wants to see its code, since it wants to instantiate a copy of it with the actual value of S
. If it doesn't have the code available, it assumes the code is provided otherwise, and doesn't generate any code:
A.hpp
template<int S> class A {
public:
A() { /* .... */ }
int MaxLength(); /* not defined here in the header file */
};
A.cpp
template<int S> int
A<S>::MaxLength() { /* ... */ }
If you now only include include A.hpp for the code using the class template A, then the compiler won't see the code for MaxLength
and won't generate any instantiation. You have two options:
S
you know you will use. For those values, you won't need to provide the code of MaxLength
For the second option, this is done by putting a line like the following inside A.cpp:
template class A<25>;
The compiler will be able to survive without seeing code for the member functions of A<25>
now, since you explicitly instantiated a copy of your template for S=25
. If you don't do any of the two options above, the linker will refuse to create a final executable, since there is still code missing that is needed.
Upvotes: 2
Reputation: 180225
A<S>::MaxLength()
is so trivial it will be fully inlined. Hence, there will be 0 copies. A<S>::A()
looks more complex, so it likely will cause multiple copies to be generated. The compiler may of course decide not to, as long as the code works as intended.
You might want to see if you can move the loop to A_base::A_base(int S).
Upvotes: 1
Reputation: 4523
The template will be instantiated for each different values of S.
If you move the method implementations to a different file, you'll need to #include that file. (Boost for instance uses the .ipp
convention for such source files that need to be #included).
If you want to minimise the amount of code that is generated with the template instantiation (and hence needs to be made available in the .ipp
file) you should try to factor it out by removing the dependency on S. So for example you could derive from a (private) base class which provides member functions with S as a parameter.
Upvotes: 5
Reputation: 6836
Compiler will create different instances of the class if you instantiate it for different types or parameters.
Upvotes: 0
Reputation: 71053
It will create two different versions of A()
and MaxLength()
that will return compile-time constants. The simple return S;
will be compiled efficiently and even inlined where possible.
Upvotes: 0