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.
Related
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.
MCVE's speak louder than words:
// int bar();
template <bool B> class Foo {
friend int ::bar() { return 123; }
};
int main()
{
Foo<false> f1;
Foo<true> f2;
}
with GCC 6 and --std=c++14, this gives me:
a.cpp: In instantiation of ‘class Foo<true>’:
a.cpp:9:12: required from here
a.cpp:3:13: error: redefinition of ‘int bar()’
friend int ::bar() { return 123; }
^~
a.cpp:3:13: note: ‘int bar()’ previously defined here
Now, I'm not sure what the standard says; but I know that the compiler knows that the friend is not templated on B, nor does its definition use B. So why can't it apply the "oh, all inline copies of the same definition of a function are the same" rule?
Now, I'm not sure what the standard says;
This case has in fact been clarified with an example in the upcoming C++17
[temp.inst]/2 The implicit instantiation of a class template specialization ... [snip] ... for the purpose of determining whether an instantiated redeclaration of a member is valid according to 3.2 [basic.def.odr] and 9.2 [class.mem], a declaration that corresponds to a definition in the template is considered to be a definition. [ Example:
... [snip (another example)] ...
template<typename T> struct Friendly {
template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff; // ill-formed: produces second definition of f(U)
— end example ]
Admittedly as you point out, the example of the standard does produce a different definition for each instantiation, but that is not necessary for the example to be ill-formed according to that rule.
So why can't it apply the "oh, all inline copies of the same definition of a function are the same" rule?
This question seems to apply to the a much simpler situation as well:
inline void foo(){}
inline void foo(){}
Surely a compiler can see that the definitions are identical, just as much as a compiler can see that the definition of your ::bar depends not on the template argument of Foo.
Yet, odr says that the re-definition is ill-formed. This is true for definitions outside a class template, as well as definitions that are caused by instantiation of a class template.
Perhaps odr could be relaxed for the case that you demonstrate, but that would require complicating the standard with a special case rule, and complicate the compilers that then would have to analyse whether template arguments are used within the definition, so such relaxation certainly isn't without compromise.
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);
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.
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.