Recently when I was trying to optimize my include hierarchy I stumbled upon the file a.hpp:
template<class T>
class A
{
using t = typename T::a_t;
};
class B;
extern template class A<B>;
which seems to be ill-formed. In fact it seems as if the extern template statement at the end causes an instantiation of A<B> which causes the compiler to complain about an incomplete type.
My goal would have been to define A<B> in a.cpp:
#include <b.hpp>
template class A<B>;
This way I avoid having to include b.hpp from a.hpp which seems like a good idea to reduce compile time. However it does not work (a.hpp on itself doesn't compile!) Is there a better way of doing this?
Note: Of course I could just not use explicit template instantiation but this is not what I want! I would like to "precompile" A<B> to save compilation time if it were used, but if A<B> is not used I don't want to include b.hpp in every file that uses a.hpp!
The extern template declaration prevents the instantiation of member function bodies, but it forces the instantiation of the class definition, since the compiler needs that anyway, and the class body needs a full definition of the template argument since it accesses its members. I'm afraid that hiding B's body from users of A<B> is not possible.
extern template is an optimization, but it doesn't change the fundamental workings of the instantiation mechanism.
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1448.pdf
The extern specifier used to declare an explicit instantiation of a
class template only suppresses explicit instantiations of definitions
of member functions and static data members not previously specialized
in the translation unit containing the declaration.
Thus, if a definition of B is required in A, you cannot use an extern template without knowledge of B. You could of course try to get rid of that requirement. In the given case, you could remove the using t declaration and have a meta function to yield that type:
template<typename T>
struct get_a_t;
template<typename T>
struct get_a_t<A<T>>
{
using type = typename T::a_t;
};
Not sure it that is feasible in your case. As soon as A needs to store a B or a B::a_t, you need B. References and pointers would be OK, though.
The final extern template class A is telling the compiler that there is in some compilation unit a declaration of such template specialization. The compiler goes on and then the linker should complain about not finding the correct class. It is not ill formed; it depends on the use case. You can define in a separate cpp file the template A. This will obviously just reduce a bit the compile time if you compile it over and over. You can do different structures:
one a.hpp with just the class A template.
one b.cpp file with the class B along with its .h file. (is it a template?)
b.cpp includes a.hpp and inside you make an explicite template instantiation template class A; (not with extern).
At this point whenever you need to use that template you can just write
extern template class A;
in your file and link the compiled b.cpp file. If you include the a.hpp file since you still need the template you won't recompile it since you have the extern command.
Related
Consider this. There is a class Derived that inherits from some instantiation of a heavy templated class Base, and there are many uses of Derived in various source files. So it is reasonable to have only one Derived-specific instantiation of Base.
C++11 allows this via extern template, but the problem here is that I want to type the Derived-specific instantiation of Base only once. Technically it is possible, as Derived can hold an alias for that instantiation, but the question is: will it still force the compiler not to instantiate the template?
Here is the try:
// Base.hpp
template < typename Arg > struct Base { using TheArg = Arg; };
// Derived.hpp
#include "Base.hpp"
struct Derived : Base<int> { };
// The next line will be huge in real-world scenario, so I want to avoid it.
// extern template struct Base<int>;
// Instead I want this.
extern template struct Base<Derived::TheArg>;
// Derived.cpp
#include "Derived.hpp"
template struct Base<Derived::TheArg>;
// Use01.cpp
#include "Derived.hpp"
void use01() { Derived dd; }
The point here is to force Use01.cpp not to instantiate Base<int> and to refer to the explicit instantiation at Derived.cpp instead.
I am compiling with gcc-v9.3 and the questions are:
Does extern template declaration takes effect to all instantiations in the translation unit, or only to those which appear after its declaration?
Will using Derived::TheArg instead of int cause any problems with deferring the instantiation?
Putting the extern template declaration at the end of Use01.cpp and commenting out the explicit instantiation at Derived.cpp makes the compilation to fail, so this gives me some confidence that the extern template declaration doesn't have to appear before any instantiation, so the second question still makes sense.
An explicit instantiation declaration of a specialization of a class template doesn’t prevent instantiating that specialization. After all, you still need to be able to refer to members of the resulting class, which means knowing everything about its layout. What it does do is prevent instantiation of its (non-inline) member functions, so that no code need be emitted for them.
Additionally, an explicit instantiation declaration must precede any source of implicit instantiation to be effective. If you want to name that specialization’s template argument just once, introduce a type alias for it before the explicit instantiation declaration and the definition of Derived.
What I am trying to do:
// in foo.hpp
class foo {
private:
int m_a;
long m_b;
public:
foo(); // default constructor (defined in foo.cpp)
template<class T>
int Func1(int a, int b) {
// Func1 definition
// uses member variables m_a, m_b
}
}
// in foo.cpp
#include "foo.hpp"
foo::foo() {
// Foo constructor
// Uses member variables m_a, m_b
}
I understand templates in C++ work as a pattern and are instantiated on demand. Therefore, for the compiler to understand whats happening, the template method definition needs to be in the header file (or else I need to use the other workarounds like using an implementation file (.tpp) and including it in foo.hpp)
This also applies to a template class with non-template methods. The non-template methods need to be defined in the header file.
My question is Foo is a regular class and has one template method and a regular constructor. Should the constructor be defined in the header file or can it be defined in a separate source file?
I ran into this issue at work. Moving the constructor definition to the header file seems to fix the issue.
It would be helpful if someone could provide an explanation for this behavior.
Should the constructor be defined in the header file or can it be defined in a separate source file?
As long as the constructor and the class itself is not a template, you can define it wherever you would like.
One point I would like to clarify: A templates definition simply needs to be visible at the point of instantiation. This does not actually mean that a template must always be defined in a header; it can just as easily be defined in the source file, if its only uses are meant to be internalized within the source file (e.g. it's a private function, only used internally).
Often this does mean that it should be in a header for cases where it's either a public function template, or a class template -- but there are exceptions to this rule.
consider the following code:
//header.h
template<class T>
class A
{
static int x;
};
template<class T>
int A<T>::x = 0;
//source1.cpp
#include "header.h"
void f(){} // dummy function
//main.cpp
#include "header.h"
int main(){}
In this case code compiles perfectly without errors, but if I remove the template qualifier from class
class A
{
static int x;
};
int A::x = 0;
In this case compiler erred with multiple definition of x. Can anybody explain this behavior?
And when the template class's static variable is initialized / instantiated??
Compiler will remove duplicate template instantiations on its own. If you turn your template class into regular one, then its your duty to make sure only one definition of static variable exists (otherwise linker error will appear). Also remember that static data members are not shared between instatiations of templates for different types. With c++11 you can control instatiations on your own using extern templates: using extern template (C++11).
As for the point of instatiation for static members:
14.6.4.1 Point of instantiation [temp.point]
1 For a function template specialization, a member function template specialization, or a specialization for a
member function or static data member of a class template, if the specialization is implicitly instantiated
because it is referenced from within another template specialization and the context from which it is referenced
depends on a template parameter, the point of instantiation of the specialization is the point of
instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization
immediately follows the namespace scope declaration or definition that refers to the specialization.
so point of instatiation should be ie. right after main() if you use your type for the first time inside main().
Templates as the name suggest are the the fragments of code that will be used several times for different parameters. For templates the compiler is to ensure if their methods and static fiels definition are linked only ones. So if you create a static field with its default value the compiler is obliged to provide single memory cell (for the same template parameter set) even though the template class header is included several times. Unfortunetly non-template classes you need to be managed by yourself.
As for the second question, I believe the standard does not state when the static fields need to be initialized, each compiler can implement then it in its own manner.
It is necessary to instantiate/initialize static members in cpp files not in headers. Static members are property of class not property of objects, so if you include header file in more cpp files, it looks like you are initializing it more times.
Answer to this question is more complex. Template is not one class. It is instantiating on demand. It means that every different use of template is one standalone "template instance". For example if you use A<int> and A<float> than you will have 2 different classes therefore you would need to initialize A<int>::x and A<float>::x.
For more info see this answer: https://stackoverflow.com/a/607335/1280316
A class (be it a template or not) can (and should) be declared in any compilation unit that referes to it.
A static field initialization does defines a variable, and as such it should exist only in one compilation unit -> that's the reason why you get an error when class A is not a template.
But when you declare a template, nothing is really created untill you instantiate it. As you never instantiate the template, the static field is never defined and you get no error.
If you had two different instantiations in source1.cpp (say A<B>) and main.cpp (say A<C>) all will still be fine : you would get A<B>::x in source1 and A<C>::x in main => two different variables since A<B> and A<C> are different classes.
The case where you instantiate same class in different compilation units is trickier. It should generate an error, but if it did, you could hardly declare special fields in templates. So it is processed as a special case by the compiler as it is explained in this other answer to generate no errors.
Context
We develop a templated settings system class, that will be part of an API we expose to users of different architectures (fewer words : we should not rely on compiler-specific behaviors)
The generalized code would look like :
In the header
namespace Firm
{
// Class definition
class A
{
template<class T>
void foo(T* aParam);
};
// Non specialized template definition
template<class T>
void A::foo(T* aParam)
{
//...
}
// Declaration of a specialization
template<>
void A::foo<int>(int* aParam);
} // namespace
In the CPP file
namespace Firm
{
// Definition of the specialized member function
template<>
void A::foo<int>(int* aParam)
{
//...
}
} // namespace
Questions
Everything runs fine with gcc 4.x. (i.e. Different compilation units use the specialized methods when appropriate.) But I feel uncomfortable since I read the following entry :
Visibility of template specialization of C++ function
Accepted answer states, if I correctly understand it, that it is an error if the definition of the specialization of a template method is not visible from call site. (Which is the case in all compilation units that are not the CPP file listed above but include the header)
I cannot understand why a declaration would not be enough at this point (declaration provided by the header) ?
If it really is an error, is there a correct way to define the specialization for it to be :
non-inline
usable in any compilation unit that includes the header (and links with the corresponding .obj) ?
Put the declaration in the header file.
The compiler either needs to instantiate the template (needs the definition) or you need to use c++0x extern templates
look here:https://stackoverflow.com/a/8131212/258418
edit
# Single definition rule:
YOu should be fine since I expirienced the following behaviour for templates:
Create them in the different o files (multiple time, time consuming due to several compilations), when the linker comes in it removes the duplicates and uses one instantiation.
also look at this answer/wiki link: https://stackoverflow.com/a/8133000/258418
edit2
for classes it works like this:
extern template class yourTemplate<int>; //tell the compiler to just use this like a funciton stub and do no instantiation. put this in the header
template class yourTemplate<int>; //instantiation, put this in a c file/object that you link with the project
for your function it should work like this:
// Declaration of a specialization in your header
extern template<>
void A::foo(int* aParam);
//in your cpp file
template<>
void A::foo<int>(int* aParam) {
code...
}
template class A::foo<int>(int* aParam);
I just read the wiki article about CRTP, and I'm a little confused about template instantiation.
According to the wiki,
member function bodies (definitions) are not instantiated until long
after their declarations.
I don't quite understand what it means.
Suppose I got a class template:
template <typename T>
class A
{
public:
void foo(T t)
{
//...
};
};
When I instantiate the class template A, does it instantiate the member function foo()?
For example:
//in .cpp file
int main()
{
A<int> a; //question 1
//class template is instantiated here, isn't it?
//What about foo(), is it instantiated too?
a.foo(10); //question 2
//according to the quotation, foo() will not be instantiated until it is used.
//if so, foo() is instantiated right here, not in question 1, right?
}
You seem to be confusing one thing:
Instantiation happens during compilation, not during runtime. Hence you can't say "on which line" a class template or a function template was instantiated.
That said, you're right about the fact that member function templates aren't instantiated together with class templates.
You could observe it in such a case: You have the following files
template.h (defines class A and function A::foo)
a.cpp (uses A)
b.cpp (uses A and A::foo)
Then during compilation of a.cpp, only A would be instantiated. However, during compilation of b.cpp, both would be instantiated.
Because of this, in case A::foo contained some semantically invalid code for a given set of template parameters, you would get compile errors in b.cpp, but not a.cpp.
I hope that clears things up!
With class templates, the rule of thumb is that only those members are instantiated which are actually used.
If you want complete instantiation, C++ offers explicit instantiation (however, usually you don't; the fact that not every bit is fully instantiated means that your template class is even more generic as it lowers the requirements on T, note that syntax checking and lookup of non-dependent types (stuff that is not dependent on T) still happens).
You will find a more complete answer here: Template instantiation details of GCC and MS compilers