I have a template that I do not want to be implicitly instantiated for T=int
. Therefore, I declare it as extern for T=int
(I do not want a specialization). I do this so I can instantiate the template (meta-)code in a different file, which will avoid unnecessary recompilation of foo<int>()
if only main()
changes, but not foo()
.
This is my code:
test.cpp:
template<class T>
auto foo()
{
return;
}
extern template auto foo<int>();
int main()
{
foo<int>();
return 0;
}
As expected, this code does not compile if I use g++ test.cpp
. However, when I compile it using g++ -O test.cpp
, it compiles successfully.
I can't seem to find out why this is the case. I'm suspecting it has to do with this paragraph from CPP-reference: function template:
Explicit instantiation declarations do not suppress the implicit instantiation of inline functions, auto-declarations, references, and class template specializations.
This suspicion comes from the fact that this program:
test2.cpp
template<class T>
void foo()
{
return;
}
extern template void foo<int>();
int main()
{
foo<int>();
return 0;
}
where auto
was substituted by void
, does not compile for any optimization level (as it should).
But my understanding of optimization levels thus far was that any optimization level satisfies the standard, so even if test.cpp
is supposed to compile because of the quoted paragraph, it should compile on any optimization level.
Is my understanding of optimization levels wrong, or might this be a bug in g++? My g++ version is 12.2.0.
Thanks for any help!
(I also found a similar example that compiles using -O2
, but fails to do so on -O
. Left it out for brevity so far, let me know if it would be helpful.)
When a function template has a deduced return type, the idea of preventing implicit instantiation doesn't make sense. It's impossible for the compiler to even determine whether a function call is well-formed, unless it knows the return type. And that means it must instantiate the body here in this translation unit.
According to [temp.inst]/1, when a function has a deduced return type, it is not considered to be a declared specialization even though it has an explicit instantiation declaration. Only declared specializations can have their implicit instantiation suppressed.
So, if you want to suppress implicit instantiation, you must avoid the deduced return type.
The reason why you see different behaviour depending on the optimization level has to do with [temp.explicit]/13:
[...] An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.
This is very similar to what happens when you declare a function and don't define it, but you do odr-use the function: you violate the one-definition rule and the program is ill-formed, no diagnostic required. It might compile and link anyway, most likely because the compiler has either inlined the call or optimized it out entirely.
In your case, -O
has probably caused the compiler to optimize out the call to foo<int>
.