Why does the compiler instantiate templates while I ask him not to? - c++

I would like to prevent the compiler from implicitly instantiating some template with extern template.
The following snippets works as expected (the static_assert does not trigger):
template<typename T>
void f() {
static_assert(sizeof(T) == 0, "f()");
};
extern template void f<int>();
void g() {
f<int>();
}
But with this one it seems that the compiler tries to instantiante the template function, as the static_assert does trigger:
struct S {
template<typename T>
void f() {
static_assert(sizeof(T) == 0, "S::f()");
}
};
extern template void S::f<int>();
void g() {
S s;
s.f<int>();
}
with this one, the static_assert also triggers whereas I would expect that it does not:
template<typename T>
struct S {
S(){};
static_assert(sizeof(T) == 0, "S");
};
extern template struct S<int>;
void g() {
S<int> s;
}
In my actual cases, I would like to speedup compilation times, but I observe that the compilation of translation units where there is an extern template ..., the symbols related to these templates does not appears in the .o file (looking with nm), but they are actually compiled... (I check that through the observation of significant compilation times with clang's -ftime-trace and with templight++).
Why does extern template does not seems to work as expected?
Thanks!

Explicit instantiation declarations can’t suppress all instantiation: a class’s members must still be known to use an object of that type, after all, and inline functions must have their definitions known in order to be inlined.
This example is just both of those in quick succession: the class template can’t “protect” the member function template, and that member function template is inline because it’s defined in its class.
The latter rule is a bit arbitrary: it was once considered necessary for ODR reasons, but the modern understanding is that the “merging” of the whole class (template) definition is sufficient. Accordingly, C++20 removes that implicit inline in modules, where it interacts badly with linkage rules.

Related

Does an explicit instantiation declaration of a member function of a class template cause instantiation of the class template?

[dcl.spec.auto]/14 states [emphasis mine]:
An explicit instantiation declaration does not cause the instantiation of an entity declared using a placeholder type, but it also does not prevent that entity from being instantiated as needed to determine its type. [ Example:
template <typename T> auto f(T t) { return t; }
extern template auto f(int); // does not instantiate f<int>
int (*p)(int) = f; // instantiates f<int> to determine its return type, but an explicit
// instantiation definition is still required somewhere in the program
 — end example ]
and [temp.explicit]/11 states [emphasis mine]:
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.
Now, consider the following program:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
};
// explicit instantiation declarations
extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();
int main() {}
This is well-formed; [temp.explicit]/11 does not apply as neither member function of class template specialization entities Foo<void>::foo() nor Foo<int>::foo() are used in a way that would otherwise cause an implicit instantiation, as per [dcl.spec.auto]/14(1).
Now, consider if we defined a friend function at its friend declaration in the class template Foo:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
friend void bar() { }
};
void bar();
If any more than one specialization of Foo is instantiated in the same translation unit, [basic.def.odr]/1 will be violated:
No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.
as the friend bar() would be re-defined(2) for each specialization that is instantiated.
According to the argument above, the explicit instantiation declarations of the two member function (of class template) specializations should not lead to any instantiation of the associated class template (as per [dcl.spec.auto]/14), meaning the following program should also arguably be well-formed:
template <class T>
struct Foo {
static const auto& foo() { static T t; return t; }
friend void bar() { }
};
void bar();
extern template const auto& Foo<void>::foo();
extern template const auto& Foo<int>::foo();
int main() {}
However, both Clang (10.0.0) and GCC (10.1.0) rejects the program (C++14, C++17, C++2a) with a "redefinition of void bar()” error:
Clang
error: redefinition of bar
note: in instantiation of template class Foo<int> requested here:
extern template const auto& Foo<int>::foo();
GCC
In instantiation of struct Foo<int>:
error: redefinition of void bar()
But I never requested (or, afaict, used these specializations in a way such that) the Foo<int> or Foo<void> specializations (are) to be instantiated.
Thus to the question:
Is the program (with the friend) above well-formed, or are the compilers correct to instantiate the class template specializations and subsequently reject the program?
(1) Note the the same question (and compiler behaviour) applies even if foo() is not declared using a placeholder type, but then we would not be able to fall back on the explicitness of [dcl.spec.auto]/14, but we may not need to.
(2) As friends defined at their friend declaration are inline, we may actually instantiate different specializations in different translation units and still respect ODR, but this is not relevant in this discussion.
The argument that the class template must be instantiated is that declaration matching may need to know things about the class that plainly require instantiation. Consider the simplified example
template<class T>
struct A {void f(T) {}};
extern template void A<int>::f(int);
To know whether the member function exists, we must instantiate the declaration in the class template, and we can’t do that in general without instantiating the whole class: the parameter type could depend on any other declarations in the class template, and we might need to consider multiple overloads or even do template argument deduction to decide which f is meant. One can argue that instantiation should happen only if one of these situations actually pertains, which strays into CWG2 territory (where instantiation is obviously impossible), but the idea is that instantiation is necessary in principle to decide about such questions because we simply don’t try examining the template itself first.

gcc inline the generic template function with a specialized definition

I doubt if gcc 4.8.3 inlines incorrect template functions... This problem does not occur in debug mode, but only in optimized mode. However this happens in a complicated code base, I am not able to reproduce the issue in a simple test case.
My code is like the following
#include "stdio.h"
class A {
public:
template<typename T> int WriteNative(const T) {
printf("here?\n")
return 0;
}
template<typename D>
void doit() {
if (WriteNative<double>(1)) {
printf("A\n");
} else {
printf("B\n");
}
}
};
// in my real code, this definition is in a different cpp file
template<> int A::WriteNative<double>(const double) {
return 1;
}
int main() {
A a;
a.doit<float>();
}
In debug build, it prints out A, while in optimized build, it prints out here?\nB
I guess any inliner uses the generic template function definition but not the specialized one. But attribute ((noinline)) does not help.
Does anyone if my code has a defined behavior of C++? and how to fix this issue?
You don't state this explicitly, but I'd guess in your actual code you were failing to declare the specialization in A's .h file. So when A.h was included in a separate compilation unit, the compiler was unaware of the specializatoin of WriteNative(). Adding a declaration of the specialization should fix the issue without having to include the definition in the same file (i.e., not having to inline it):
class A {
public:
template<typename T> int WriteNative(const T) {
printf("here?\n")
return 0;
}
template<typename D>
void doit() {
if (WriteNative<double>(1)) {
printf("A\n");
} else {
printf("B\n");
}
}
};
template<> int A::WriteNative(const double);
You can reproduce your issue by using three files A.h, A.cpp, and main.cpp, where A.cpp contains the definition of the specialization, so that when main.cpp includes A.h, it is unaware of the specialization when inlining happens during optimization, and when compiled with -O0, no inlining occurs, so WriteNative() gets linked against the definition in A.cpp.
Edit:
See this answer which cites the spec to explain why this is correct behavior.
14.7.3 [temp.expl.spec]:
6/ If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared
before the first use of that specialization that would cause an
implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and
either the specialization is used in a way that would cause an
implicit instantiation to take place or the member is a virtual member
function, the program is ill-formed, no diagnostic required. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.

Declare function template instantiation without the template

Is it possible to declare that a symbol is an explicit instantiation of a function template, without defining the function template first?
It would express to the compiler that there exists a function template in another translation unit that is instantiated somewhere, and we want to call the instantiated function.
// declaration of instantiation, perhaps it would look like one of these:
// template<typename> void foo(int);
// template<typename> void foo<int>(int);
void bar(int i) {
// definition of function unknown to caller, it shouldn't matter
foo(i);
// either that or this perhaps:
foo<int>(i);
}
Is there a technical reason this can't be done or is it just for lack of syntax? Is there a reason that it's not possible to provide sufficient information in a declaration to generate calls to an instantiated function template?
There is no Y behind this X. This question is meant literally. It's an abstract question about the C++ language. I could provide an example that doesn't compile but that would just be a distraction.
The question is also not about specialization per se. Whether the template was specialized shouldn't matter. This question is only concerned with declaring that a template exists and that it was instantiated.
Related question: How do I explicitly instantiate a template function? - however that does not solve this problem, as it requires the full template definition to be visible.
You can use "extern template" here. It tells the compiler not to instantiate it in every translation units. This is part of C++11 enhancements. For example, we may declare the template in a header file a.hpp as
// a.hpp
template <class T>
T fun(T& a);
Then in a.cpp
// a.cpp
#include "a.hpp"
extern template int fun(int&);
int main()
{
int a = 100;
return fun(100);
}
And in b.cpp we can actually instantiate the template:
// b.cpp
#include "a.hpp"
template <>
int fun(int& x)
{
return x + 1;
}
To add to Nipun Talukdar's answer, there's no need for specialization or for the template definition to be visible to the caller.
a.cpp
template<class T> T fun(T);
extern template int fun(int);
int main() {
// this works, even though we have no idea how fun() is defined
fun(100);
// this would fail to link
// fun('a');
}
b.cpp
template<class T>
T fun(T x) {
return x;
}
// explicit instantiation
template int fun<int>(int x);

Specialization of member function template after instantiation error, and order of member functions

The following bit of code fails to compile on gcc 4.5.3
struct Frobnigator
{
template<typename T>
void foo();
template<typename T>
void bar();
};
template<typename T>
void Frobnigator::bar()
{
}
template<typename T>
void Frobnigator::foo()
{
bar<T>();
}
template<> // error
void Frobnigator::foo<bool>()
{
bar<bool>();
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Error message: specialization of ‘void Frobnigator::bar() [with T = bool]’ after instantiation. I finally resolved this problem by having the specialization of Frobnigator::bar<bool>() appear before Frobnigator::foo<bool>(). Clearly the order in which the methods appear matter.
Why then is the following lite version of the above code, in which the the specialization of bar appears after the generic version, valid ?
struct Frobnigator
{
template<typename T>
void foo();
};
template<typename T>
void Frobnigator::bar()
{
}
template<>
void Frobnigator::bar<bool>()
{
}
int main()
{
}
Your first code is not correct by standard.
n3376 14.7.3/6
If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required.
In your case - implicit instantiation of bar function with type bool is required by its usage in foo<bool>, before explicit specialization declaration.
Clearly the order in which the methods appear matter.
Indeed; as is usually the case in C++, you can't use something before it's declared, and this applies to explicit template specialisations as well as most other things.
Using bar<bool> (by calling it from foo<bool>) without a previous declaration of an explicit specialisation causes that specialisation to be instantiated from the generic template, if it hasn't already been. You'll need at least a declaration of the explicit specialisation to prevent that.
Why is this the case, considering that the specialization of bar appears after the generic version in the following lite version of the above code
The second example differs by not instantiating foo<bool> at all. The issue isn't that the specialisation is declared after the generic template (which must be the case), but that it's declared after that specialisation has already been instantiated.

What's the right way to specialize a template when using "extern template"?

I am hoping someone can point out the correct way to specialize a method in a template class while using "extern template class" and "template class" for explicit instantiation with gnu c++. I've tried to boil down this problem with the simplest example that mimics my real problem. It appears that declaring "extern template" implies a template instantiation which causes errors when specializing methods. Given a driver program:
main.cc
#include A_H
#include <iostream>
int main()
{
A<int> ai;
A<long> al;
std::cout << "ai=" << ai.get() << " al=" << al.get() << std::endl;
return 0;
}
And the following implemntation of A
a.h
template<typename T>
struct A
{
int get() const;
};
extern template class A<int>;
extern template class A<long>;
a.cc
#include "a.h"
template<typename T>
int A<T>::get() const
{
return 0;
}
template<>
int A<long>::get() const
{
return 1;
}
template class A<int>;
template class A<long>;
I receive the following error when compiling with either, g++ 4.1.2 or 4.4.4
% g++ -Wall -g -D'A_H="a.h"' a.cc main.cc
a.cc:10: error: specialization of 'int A<T>::get() const [with T = long int]' after instantiation
%
If I comment out the two "extern template" lines in a.h, things compile and work as expected with both compilers. I assume depending on the existence of an explicit instantiation in the absence of "extern template" is unspecified behavior even in C++0x, otherwise, what's the point of C++0x adding "extern template"?
If I instead implement A as:
a-hack.h
template<typename T>
struct A
{
int get() const;
};
template<typename T>
int A<T>::get() const
{
return 0;
}
template<>
inline
int A<long>::get() const
{
return 1;
}
extern template class A<int>;
extern template class A<long>;
a-hack.cc
#include "a-hack.h"
template class A<int>;
template class A<long>;
and compile again, this works as expected
% g++ -Wall -g -D'A_H="a-hack.h"' a-hack.cc main.cc
% ./a.out
ai=0 al=1
However, in my real world example, this causes a program crash with g++ 4.1.2 (while working for g++ 4.4.4). I have not narrowed down the exact cause of the crash (segmentation fault). It only appears as if the stack pointer is corrupted within what would be the call to A<>::get().
I realize that explicit template instantiation is non-standard at this point, but would anyone expect what I've done above to work? If not, what is the correct way to do this?
Thanks
extern template class A<long>;
This line says that A<long> is to be explicitly instantiated according to the definitions the compiler has already seen. When you add a specialization later, you break that meaning.
Add a declaration of your specialization to the header file.
template <typename T> struct A { /*...*/ };
template<> int A<long>::get() const;
extern template class A<int>;
extern template class A<long>;
In general, it's best to put as many specialization declarations as possible in the same header file as the primary template, to reduce surprises for the compiler about which declaration should be used for any particular instantiation.
Notice that the extern template declaration isn't necessary if you're dealing with a single template entity (as opposed to this case, where we have to instruct the compiler about both the class A<long> and the function A<long>::get()). If you want to specialize a function template in another translation unit, it suffices to write just template<>.
template<typename T> int freeGet() { return 0; } // you can even add "inline" here safely!
template<> int freeGet<long>(); // this function is not inline (14.7.3/12)
But you must have the <> there. If you omit the <>, the declaration turns into an explicit instantiation of the default implementation (return 0), which is likely not what you wanted! Even if you add extern, the compiler is allowed to inline that default implementation; if your code unexpectedly breaks when you pass -O2, you might have accidentally omitted the <> somewhere.
Adding this answer to address the question in the title (template instantiation, and not necessarily template method instantiation).
This much resembles function declaration/definition.
Explicit instantiation declaration: extern template class is a declaration and should generally go in the header.
Explicit instantiation definition: template class is a definition and should generally go in the cpp.
No code is generated in object files following the declaration.
Code is generated in the object file of the cpp that contained the definition.
Without explicit instantiation, implicit instantiation will take place when the template is actually used. This will happen in every compilation unit (object file) that uses the template.
Implicit instantiation won't take place if a declaration was encountered. This is the gist of this mechanism - avoid object code duplication caused by implicit instantiation that happened because the compiler didn't trust that there's a single compilation unit in charge of instantiating the template.
More info here.