questions about c++ templates
9 answers - 1847 bytes -

Friday 18 August 2006 09:11, B Jacob wrote:
Hi!
I've got a few questions, so if you can help me that'll result in a better
kdesupport/eigen library ;)
1. when you instantiate a template class, does the compiler generate code
only for those methods that are getting called for this particular template
instantiation, or does it generate code for all methods?
Concretely:
template<class Tclass M
{
// 500 methods that DN'T call one another
void method1();
void method500();
};
M<intm;
m.method1();
does that generate code for the 499 unused methods of M<int?
No. (Although it may be compiler dependent; a recent version of g++ does it
right)
2. when you instantiate the same template class for various pointer types,
does the compiler generate code only once ?
Concretely:
M<int *mi;
M<SomeClass *mc; // does that generate code for M<SomeClass *or does
// that share code with M<int *?
No.
3. In my code, I have a class A (it's an assignable data type) and it would
be natural for me at some point to do
QList<Al;
and then to use only a few elementary methods of QList<A(like append
elements and do a foreach). How much binary size would be replaced by doing
QList<A *l;
That would be less convenient for me, so I'd like to know if it's worth it.
The difference would be n * sizeof(A) - n * sizeof(A*) where n is the number
of elements in your list. The size of the generated template methods would
not change (although stack allocations within the generated template methods
would have a different size and the first version may call constructors,
while the second version would not).
Hope that helps.
No.1 | | 799 bytes |
| 
Friday 18 August 2006 09:52, B Jacob wrote:
hey, thanks a lot for your answers. question though:
M<SomeClass *mc; // does that generate code for M<SomeClass *or
does // that share code with M<int *?
No.
when you answer "No" here, does it mean, "No, it doesn't generate code for
M<SomeClass *>", or does it mean, "No, it doesn't share code with M<int *>"
?
If it doesn't share code, do you have a clue why ? At first sight, one
would think that from a low-level point of view, all pointer types are
equivalent (to, say, void*) and thus there shouldn't be any difference
between M<some_type *and M<some_other_type *>, so code could be shared
?
Sorry, that was unclear. No it does not share code.
No.2 | | 2302 bytes |
| 
Friday 18 August 2006 09:52, B Jacob wrote:
hey, thanks a lot for your answers. question though:
M<SomeClass *mc; // does that generate code for M<SomeClass *or
does // that share code with M<int *?
No.
when you answer "No" here, does it mean, "No, it doesn't generate code for
M<SomeClass *>", or does it mean, "No, it doesn't share code with M<int *>"
?
If it doesn't share code, do you have a clue why ? At first sight, one
would think that from a low-level point of view, all pointer types are
equivalent (to, say, void*) and thus there shouldn't be any difference
between M<some_type *and M<some_other_type *>, so code could be shared
?
Grrr, hit the send button too early.
It can't share code, because you could have the following:
template<typename T>
void f(T t)
{
t->g();
}
Where some_type may have g(), but some_other_type may not. , in a more
complicated situtation, they may both have g(), and both g()s are declared
virtual, but each g() lives in different spots in the vtable, so the same
calling code would end up calling different methods if the offset into the
vtable was the same because they were running the same generated code.
Now if you can guarantee none of the above situations are going to happen (ie
two classes with identical definitions), then I suppose you _could_ merge the
two. I'm not sure what the standard says. However, that's a fair bit of
work for the compiler for what's likely a rare situation. g++ does not do
it.
Here's a test program to confirm:
#include <iostream>
using namespace std;
class T1
{
public:
void g() { cout << "T1" << endl; }
};
class T2
{
public:
void g() { cout << "T2" << endl; }
};
template<typename T>
void f(T t)
{
t->g();
}
int main()
{
T1 t1;
T2 t2;
f(&t1);
f(&t2);
return 0;
}
10:02:38 [9]; nm -C ./a.out| grep 'void f'
08048708 W void f<T1*>(T1*)
08048748 W void f<T2*>(T2*)
Note that the two f()'s have different addresses.
No.3 | | 1557 bytes |
| 
Friday 18 August 2006 16:32, Craig Howard wrote:
Friday 18 August 2006 09:11, B Jacob wrote:
Hi!
I've got a few questions, so if you can help me that'll result in a
better kdesupport/eigen library ;)
1. when you instantiate a template class, does the compiler generate code
only for those methods that are getting called for this particular
template instantiation, or does it generate code for all methods?
Concretely:
template<class Tclass M
{
// 500 methods that DN'T call one another
void method1();
void method500();
};
M<intm;
m.method1();
does that generate code for the 499 unused methods of M<int?
No. (Although it may be compiler dependent; a recent version of g++ does
it right)
However, I think the picture changes a bit when a template contains virtual
functions. For example, that a template class inherits from a class that has
pure virtual functions which the template, when instantiated, implements.
In that case I believe it must expand all functions. But of course, dead code
elimination optimizations can potentially see that it's not needed, as for
any other case.
reason I like templates much is exactly that only the code one needs is
actually used. can write a complex class that accounts for much and is
convenient, but only what is actually needed is expanded.
Cheers,
Frans
>Visit #unsub to unsubscribe <<
No.4 | | 1621 bytes |
| 
Friday 18 August 2006 11:03, Frans Englich wrote:
However, I think the picture changes a bit when a template contains virtual
functions. For example, that a template class inherits from a class that
has pure virtual functions which the template, when instantiated,
implements.
In that case I believe it must expand all functions. But of course, dead
code elimination optimizations can potentially see that it's not needed, as
for any other case.
reason I like templates much is exactly that only the code one needs is
actually used. can write a complex class that accounts for much and is
convenient, but only what is actually needed is expanded.
Interesting. You are correct:
#include <iostream>
using namespace std;
class Base
{
public:
virtual ~Base() {}
virtual void pure() = 0;
};
template<typename T>
class Child : public Base
{
public:
virtual ~Child() {}
virtual void pure() {}
void g() { cout << "T1" << endl; }
};
int main()
{
Child<intc;
c.g();
return 0;
}
12:11:38 [21]; nm -C ./a.out | grep Child
080489ba W Child<int>::g()
080488de W Child<int>::pure()
080488c0 W Child<int>::Child()
0804890a W Child<int>::~Child()
08048988 W Child<int>::~Child()
08048b14 V typeinfo for Child<int>
08048b20 V typeinfo name for Child<int>
08048b00 V vtable for Child<int>
Does anybody know why that is? And why there are two destructors for Child?
No.5 | | 1681 bytes |
| 
Well, with this code, templates have no influence, at least for nm: with or
without them, there is no difference except for addresses.
-0804894a W Base::Base()
+080489ee W Base::Base()
08048920 W Base::~Base()
080488f6 W Base::~Base()
080488cc W Base::~Base()
-080489ec W Child<int>::g()
-08048a1a W Child<int>::pure()
-0804895a W Child<int>::Child()
-080489b4 W Child<int>::~Child()
-0804897c W Child<int>::~Child()
+080489c0 W Child::g()
+080489ba W Child::pure()
+080489fe W Child::Child()
+08048982 W Child::~Child()
+0804894a W Child::~Child()
Regards,
Kleag
Le vendredi 18 2006 21:13, Craig Howard a *:
Friday 18 August 2006 11:03, Frans Englich wrote:
Interesting. You are correct:
#include <iostream>
using namespace std;
class Base
{
public:
virtual ~Base() {}
virtual void pure() = 0;
};
template<typename T>
class Child : public Base
{
public:
virtual ~Child() {}
virtual void pure() {}
void g() { cout << "T1" << endl; }
};
int main()
{
Child<intc;
c.g();
return 0;
}
--
12:11:38 [21]; nm -C ./a.out | grep Child
080489ba W Child<int>::g()
080488de W Child<int>::pure()
080488c0 W Child<int>::Child()
0804890a W Child<int>::~Child()
08048988 W Child<int>::~Child()
08048b14 V typeinfo for Child<int>
08048b20 V typeinfo name for Child<int>
08048b00 V vtable for Child<int>
Does anybody know why that is? And why there are two destructors for
Child?
No.6 | | 1874 bytes |
| 
Friday 18 August 2006 14:09, Kleag wrote:
Well, with this code, templates have no influence, at least for nm: with or
without them, there is no difference except for addresses.
Right, but that's significant, because Child::pure() wasn't referenced, so it
shouldn't have been created. so I thought.
-0804894a W Base::Base()
+080489ee W Base::Base()
08048920 W Base::~Base()
080488f6 W Base::~Base()
080488cc W Base::~Base()
-080489ec W Child<int>::g()
-08048a1a W Child<int>::pure()
-0804895a W Child<int>::Child()
-080489b4 W Child<int>::~Child()
-0804897c W Child<int>::~Child()
+080489c0 W Child::g()
+080489ba W Child::pure()
+080489fe W Child::Child()
+08048982 W Child::~Child()
+0804894a W Child::~Child()
Regards,
Kleag
Le vendredi 18 2006 21:13, Craig Howard a *:
Friday 18 August 2006 11:03, Frans Englich wrote:
Interesting. You are correct:
#include <iostream>
using namespace std;
class Base
{
public:
virtual ~Base() {}
virtual void pure() = 0;
};
template<typename T>
class Child : public Base
{
public:
virtual ~Child() {}
virtual void pure() {}
void g() { cout << "T1" << endl; }
};
int main()
{
Child<intc;
c.g();
return 0;
}
--
12:11:38 [21]; nm -C ./a.out | grep Child
080489ba W Child<int>::g()
080488de W Child<int>::pure()
080488c0 W Child<int>::Child()
0804890a W Child<int>::~Child()
08048988 W Child<int>::~Child()
08048b14 V typeinfo for Child<int>
08048b20 V typeinfo name for Child<int>
08048b00 V vtable for Child<int>
Does anybody know why that is? And why there are two destructors for
Child?
No.7 | | 2542 bytes |
| 
Craig Howard (a):
Friday 18 August 2006 09:11, Bt Jacob wrote:
>Hi!
>>
>I've got a few questions, so if you can help me that'll result in a better
>kdesupport/eigen library ;)
>>
>1. when you instantiate a template class, does the compiler generate code
>only for those methods that are getting called for this particular template
>instantiation, or does it generate code for all methods?
>>
>Concretely:
>>
>
>template<class Tclass M
>{
>// 500 methods that DN'T call one another
>void method1();
>
>void method500();
>};
>>
>
>>
>M<intm;
>m.method1();
>
>>
>does that generate code for the 499 unused methods of M<int?
No. (Although it may be compiler dependent; a recent version of g++ does it
right)
Well, it depends how instantiation is done.
If you put the code in header file and any user of header can
instantiate the code, then I guess unnecessary methods are not
generated. But if you create a library (which I assume is what will be
done for eigen library) and you do explicit instantiation for some
types, then:
$ cat a.h
template<class T>
class M
{
public:
// 500 methods that DN'T call one another
void method1();
void method2();
void method500();
};
$ cat a.cpp
#include <iostream>
#include "a.h"
using namespace std;
template <class T>
void M<T>::method1()
{
}
template <class T>
void M<T>::method2()
{
}
template <class T>
void M<T>::method500()
{
}
template class M<int>;
Then:
$ g++ -c a.cpp -o a.o
$ g++ -shared a.o -o a.so
$ nm -C ./a.so | grep method
000006d6 W M<int>::method1()
000006dc W M<int>::method2()
000006e2 W M<int>::method500()
HTH
Krzysztof Lichota
>Visit #unsub to unsubscribe <<
PGP SIGNATURE
Version: GnuPG v1.4.2.2 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
TTYX88BaV+GFNIf5oBJ0k=
=+4ko
PGP SIGNATURE
No.8 | | 618 bytes |
| 
Le samedi 19 2006 04:12, Craig Howard a *:
Friday 18 August 2006 14:09, Kleag wrote:
Well, with this code, templates have no influence, at least for nm: with
or without them, there is no difference except for addresses.
Right, but that's significant, because Child::pure() wasn't referenced, so
it shouldn't have been created. so I thought.
Sorry, I missed the point. Interestingly, recompiling the template version
with , references to g() are removed from the nm output while pure() is
still present. Also the number of constructors and destructors change.
Kleag
No.9 | | 297 bytes |
| 
Craig Howard wrote:
[] And why there are two destructors for
Child?
The reason is briefly explained in this document,
Have a look at section 2.1.3 "Constructors and Destructors" on page 7.
>Visit #unsub to unsubscribe <<