4

I'm trying to move a template function's definition out of the header file.

I know that in order to do this I can explicitly instantiate this function with all the types that I want to see from the outside (during the linkage). However, inside of the source file (together with the function's definition) I have the usage (implicit instantiation) of the function with all the necessary types. This code works and links perfectly with -O0, but fails to link with -O2.

I was able to minimize the problem:

// a.cpp
#include "b.h"

int main() {
  bar<int>();
  return 0;
}
// b.h
template <class T> void bar();
// b.cpp
#include "b.h"

template <class T> void bar() {}

void foo() { bar<int>(); }

If I compile b.cpp with -O0 and with -O2 I get a different set of symbols available in the object file:

> clang++ -c b.cpp -o b.o && nm -A b.o

b.o:0000000000000000 W _Z3barIiEvv
b.o:0000000000000000 T _Z3foov

> clang++ -c b.cpp -O2 -o b.o && nm -A b.o

b.o:0000000000000000 T _Z3foov

The same happens with gcc as well.

It looks like compilers inline the instantiated function and delete it as they don't see any other usages of it.

Is it possible to keep implictly instantiated template functions from being deleted without explicitly instantiating them?

Thanks in advance!

UPD: this answer for a related question (as pointed out by @Jarod42) answers my question as well: according to the standard it is impossible to do this without an explicit instantiation.

Valeriy Savchenko
  • 1,524
  • 8
  • 19
  • The code you have shown only attempts to CALL an instantiation of the template. There is no code that actually defines the templated function, nor is there code that explicitly instantiates the template in a way that can be resolved at the call point (i.e. ensures there is a function `bar()` that can be called from `main()`). Such code is needed in your project if you want the program to link. If the code is working without optimisation, that is a compiler or linker bug. – Peter May 31 '19 at 09:45
  • First of all, thanks for a fast reply! There is a `bar` definition in **b.cpp**. `foo` calls `bar` and as we have the definition, we should generate the body for `bar`, which compiler does. Afterwards it inlines its body into `foo` and removes it. I don't quite get the notion of 'attempt to call' as the linker errors only on `main` function and not on `foo` function – Valeriy Savchenko May 31 '19 at 09:53
  • The definition in `bar.cpp` is local to that file. It cannot be called from `main()`. – Peter May 31 '19 at 10:02
  • @peter it compiles/links/runs without optimizations. With explicit instantiation (adding `template void bar();`) in `bar.cpp` it compiles/links/runs with and without optimizations even though the definition is local to `b.cpp`. – Valeriy Savchenko May 31 '19 at 10:14
  • 1
    Related/dup of [linker-error-with-implicit-instantiation-of-private-c-template-with-llvm-clang](https://stackoverflow.com/questions/24553297/linker-error-with-implicit-instantiation-of-private-c-template-with-llvm-clang) – Jarod42 May 31 '19 at 10:25
  • Yes, I read the question. Problem is, the standard does not require the instantiation you have of `bar` to be callable from `main()`. As strange as it may seem, the way your program is building without optimisation is not actually required by the C++ standard, and the failure with optimisation is closer to being right. Also, `bar` is actually implicitly instantiated in `bar.cpp`, not explicitly instantiated. Explicit instantiation is something differerent from what happens due to calling `bar` in `bar.cpp`. Anyway to find a solution to your problem, look up "external templates" – Peter May 31 '19 at 10:25
  • @peter I expected that it's not an issue with compilers and that this is a standard behavior. I hoped that there is a workaround to do it without an explicit instantiation (which I insist `template void bar();` is). As @Jarod42 pointed out, there is a similar question, and there is an [answer](https://stackoverflow.com/a/24557347/11582326) quoting the standard that there is no workaround. – Valeriy Savchenko May 31 '19 at 10:51
  • If you want to do it without explicit instantiation, then you need to fall back on implicit instantiation. The limit of implicit instantiation is that they are typically local to the compilation unit where they are instantiated - which means the definition of the templated function needs to be visible at the call point. There are some compiler-specific (or toolchain-specific) extensions to get past those constraints. Using such approaches locks you in to a specific compiler and (in some cases) a specific compiler version. That's fine if you're happy to be locked into using one compiler. – Peter Jun 01 '19 at 06:32

0 Answers0