0

I'm studing template instantiation process in C++ - Explicit instantiation declaration and definition. Having gone through few articles and documentations I realized that the explicit instantiation declaration is needed to avoid redundant template instantiation during compile time.

Is there a way to reproduce behavior with redundant template instances and find them somewhere to have a look?(what should I do to see redundant template instances?)

I tried to compile the code below with and without external template declaration. But when I decompiled .obj files(to decompile used this https://dogbolt.org/?id=16d5a9f7-b73e-4de9-b4d1-d121d70e33b1#Hex-Rays=97&Ghidra=473&BinaryNinja=91), I didn't find a place where ATest would be instantiated twice and more(perhaps I missed it because I don't understand the decompiled code pretty much). I'm not even sure that it is correct place.

//TestA.h
template <typename T, T val>
struct ATest {
    ATest();
    T foo();
    T bar();
};
//extern template struct ATest<int, 55>;
//extern template ATest<int, 55>::ATest(void);
//extern template int ATest<int, 55>::foo(void);
//extern template int ATest<int, 55>::bar(void);
//TestA.cpp
#include <iostream>
#include "TestA.h"

template <class T, T val>
T ATest<T, val>::foo(void) {
    std::cout << "Hello from foo" << std::endl;
    return val;
}

template <class T, T val>
T ATest<T, val>::bar(void) {
    std::cout << "Hello from bar" << std::endl;
    return val;
}

template <class T, T val> ATest<T, val>::ATest(void) {
    std::cout << "Hello from A()" << std::endl;
}

template struct ATest<int, 55>;
template ATest<int, 55>::ATest(void);
template int ATest<int, 55>::foo(void);
template int ATest<int, 55>::bar(void);
//MainA.cpp
#include "TestA.h"

int main()
{
    ATest<int, 55> a;
    ATest<int, 55> b;
    ATest<int, 55> c;
    ATest<int, 55> d;
    ATest<int, 55> e;
    ATest<int, 55> f;
    ATest<int, 55> g;
    return 0;
}

Any ideas would be really appreciated.

Update My question has been associated with this Why can templates only be implemented in the header file? I've read the question and 3 first answers but it is not related to my question.

My question is not about how to use temapltes in headers and why they can be implemeted in headers in C++(it is from the post pointed out above). I'm asking about compilation result of the template instances. It is rather more related to C language than what is in that post. That's why stackoverflow suggested me to choose C tag I believe.

I'm also not very experienced in C++ yet, so my question may confuse. Sorry for this then.

Update2 Ok. The second post using extern template (C++11) where my post is accosiated with explains a lot with example. But It doesn't say how and what should be decompiled to be able to see the difference. So It doesn't answer my question.

Update3(My solution) Reading this post I've realized how to achive this behavior Explicit template instantiation - when is it used?

I also misinterpreted the concept with redundant instantiation. Probably I still 100% don't understand it, but I've figured out that to get redundant instances we need to skip both: Explicit instantiation definition and declaration and create template definition in header.

Dron4K
  • 456
  • 2
  • 6
  • 20
  • Please don't tag irrelevant languages. C has no templates. – Yksisarvinen Apr 29 '23 at 18:48
  • @Yksisarvinen C was suggested by stackoverflow itself, that's why I chose it – Dron4K Apr 29 '23 at 18:49
  • @Dron4K, I hope nobody will ever suggest that you kill somebody. – Enlico Apr 29 '23 at 18:54
  • @Enlico Don't see relations... – Dron4K Apr 29 '23 at 18:56
  • Ok, I have changed the dupes, but left the first one - it's an issue in your code in any case. – 273K Apr 29 '23 at 19:25
  • Did you try to compile and link your files? You will need extern template declarations in MainA.cpp. Compile TestA.cpp and MainA.cpp separately and then link them. Also, you cannot have both `template struct ATest;` and `template ATest::ATest(void); template int ATest::foo(void); template int ATest::bar(void);`. That will result in a compiler error. – Hari Apr 29 '23 at 19:37
  • @Hari I compiled that code and it worked good with and without `extern template` in header. But when I decompiled `MainA.obj` I didn't find a difference between 2 results. I decompiled it with this https://dogbolt.org/?id=d81f50a3-4624-4ca1-891f-40fd92a36b12#Hex-Rays=97&Ghidra=473&BinaryNinja=72 – Dron4K Apr 29 '23 at 19:47
  • @Dron4K Indeed extern template declarations are not needed in MainA.cpp. My thinking was that MainA.cpp is calling the constructor ATest(). Since, you have explicitly declared ATest() in the struct definition, the compiler will not generate it. So, there will be a need to link to the constructor defined in TestA.cpp. I need to understand this further. However, with both `template struct ATest;` and `template ATest::ATest(void); template int ATest::foo(void); template int ATest::bar(void);`, there is "duplicate explicit instantiation" error in gcc. – Hari Apr 29 '23 at 20:52
  • It's weird that you are experiencing the error with gcc. I'm compiling in Visual Studio and it is working good. – Dron4K Apr 29 '23 at 21:01
  • @Dronk4K Usually, MSVC has extensions that lead to otherwise wrong code to work. – Hari Apr 30 '23 at 05:33
  • Regarding the definition of constructor `ATest()` not being visible to `MainA.cpp`: The declaration `extern ATest::ATest()` is not needed to compile `MainA.cpp` to an object file, since `MainA.cpp` sees the declaration of `ATest()` in `TestA.h`. However, it needs to link with an object file that contains the instantiation of `ATest::ATest()`. `TestA.obj` has that instantiation. "extern templates" are only needed to prevent an instantiation of a class in a TU even when all the information is available to do so. This is another use case of the `extern` keyword. – Hari Apr 30 '23 at 06:35

1 Answers1

2

You misunderstood it a bit. Template methods are not instantiated twice or more times in a single .obj, thus you don't see several ATest::* instantiations.

If you don't use extern template, then template methods are instantiated in translation units, thus the same template methods and constructs are instantiated twice or more times in all translation units where a template is used. extern template directs a compiler to not instantiate template methods in a current translation unit.

Short example

// a.h
template <typename T>
struct A {
  A(T) {}
};
// u1.c
#inlcude "a.h"
A<int> a1(1);
// u2.c
#inlcude "a.h"
A<int> a2(2);

A<int>::A(int) is instantiated twice, in each of u1.obj and u2.obj. Then a linker chooses one of them that goes into an executable.

273K
  • 29,503
  • 10
  • 41
  • 64
  • Thank you for the answer, but still not clear. What would be instantiated in u1.obj and u2.obj, if we added 'extern template' in header? – Dron4K Apr 29 '23 at 19:27
  • If you add it in the header, nothing will be instantiated in the shown example and you get the undefined reference error in a linker, until you add `template A::A(int);` somewhere. – 273K Apr 29 '23 at 19:28
  • Hmm, I think I understand what you are saying, but I cannot realize why I see the same content in .obj files when I compiled example from my post with and without 'extern template' in header... Following this logic, there should be a difference. – Dron4K Apr 29 '23 at 19:32
  • If you compile your code as is you see `ATest::ATest()` in MainA.obj. If you uncomment `extern template struct ATest;`, you DONT see `ATest::ATest()` in MainA.obj. Regardless of `extern template` you see `ATest::ATest()` in TestA.obj because of the explicit instantiation `template ATest::ATest();`. So, originally you have two instantatied `ATest::ATest()` in two .obj. – 273K Apr 29 '23 at 19:35
  • Look like I'm on the same page here with you and other sources I've read for today. But when I try to recompile my MainA.obj with these tools https://dogbolt.org/?id=d81f50a3-4624-4ca1-891f-40fd92a36b12#Hex-Rays=97&Ghidra=473&BinaryNinja=72 It doesn't give extra `ATest::ATest()` when there is no `extern template struct ATest;` in header. Probably those decompilers are not good or I use them incorrect. Anyway thank you for the clarifications – Dron4K Apr 29 '23 at 19:44
  • I'm decompiling file with .obj extension not .exe. As far as I know .exe is result of linker process. But linker will remove redundant instantiations. So it cannot contain these instantiations... – Dron4K Apr 29 '23 at 19:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/253386/discussion-between-dron4k-and-273k). – Dron4K Apr 29 '23 at 20:12
  • @273K when compiling and linking the code as is, MainA.obj doesn't have an instantiation of `ATest::ATest()`, irrespective of whether the declaration `extern template struct ATest;` is present or not. MainA.obj will be waiting for the linking stage to see `ATest::ATest()`. This is because MainA.cpp doesn't have the entire definition of `ATest`. It includes `TestA.h`, which only contains the list of class members. `TestA.cpp` has the definitions of the member functions. – Hari Apr 30 '23 at 06:06
  • @Hari Of course you can see it now easy. The first version of the question had a single code block. – 273K Apr 30 '23 at 07:22
  • @273K ok, point noted. – Hari Apr 30 '23 at 07:25