问题
The (not so new anymore) C++11 standard introduced the extern
keyword for templates. Its purpose is to tell the compiler that a template should not be instantiated at the point of usage, but that it will be instantiated in another translation unit (and thus there will be an instantiation available at link time) - at least AFAIK.
Now, even in the pre-C++11 era we used something similar to separate the declaration/definition of template classes from its instantiation in order to speed up compilation, e.g. like so:
point.h: class definition
template <int dim> struct Point {
...
void foo();
...
};
point.cpp: method definitions
#include "point.h"
template <int dim>
void Point<dim>::foo() {
...
}
point_2d.cpp: class instantiation (2D version)
#include "point.cpp"
template struct Point<2>;
point_3d.cpp: class instantiation (3D version)
#include "point.cpp"
template struct Point<3>;
main.cpp: usage of 2D and 3D points
#include "point.h"
int main(int, char**) {
Point<2> p;
p.foo();
}
Now I am wondering:
- Is our approach valid C++ (03 or 11) code or are we just lucky it worked?
- With C++11, would we be able to achieve the same thing by including
point.cpp
inmain.cpp
and declaringextern template <int dim> struct Point;
?
回答1:
Your approach is a valid C++ code and should work both in C++03 and C++11. It is called explicit instantiation:
template struct Point<2>;
While applying to class templates it explicitly instantiates all members of a class template for some template argument list in the current translation unit. With this approach it is simple to create the libraries of templates for which the set of possible template arguments is known in advance (as in your case when Point
can be 2D and 3D but not 1D, 4D etc.).
In C++11 when you add to explicit instantiation directive keyword extern
:
extern template struct Point<2>;
it becomes a declaration, not a definition. The behavior of such thing is similar to usual extern
keyword for variables. Explicit template instantiation declaration can be used together with explicit instantiation in the following manner:
// point.h
template <int dim> struct Point {
...
void foo();
...
};
extern template struct Point<2>;
extern template struct Point<3>;
#include "point.hpp"
// point.hpp
#include "point.h"
template <int dim>
void Point<dim>::foo() {
...
}
// point.cpp
#include "point.hpp"
template struct Point<2>;
template struct Point<3>;
With such code you achieve the same result as with your one but additionally you allow the users of your code to use Point<1>
, Point<4>
and other specializations of a Point
class template if they want. Without extern template struct Point<2>;
and extern template struct Point<3>;
directives Point
class template would be implicitly instantiated in the users code even for template arguments 2
and 3
that reduces the meaning of explicit instantiation (template struct Point<2>;
and template struct Point<2>;
).
回答2:
C++11 is using extern
to tell the compiler to not to instantiate a template, and the syntax is using a concrete type, unlike the suggested syntax in the question:
extern template struct Point<2>;
With C++03 the compiler must instantiate the template whenever it observes Point<2>
in a translation unit and with C++11 it knows that it must not, when coupled with the extern keyword.
To your question, what you did in C++03 was to separate the definition of the template into a separate header file (with a cpp
suffix, see below), and that approach will still work with C++11:
#include "point.ipp"
extern template struct Point<2>; // instantiated elsewhere
int main(int, char**) {
Point<2> p;
p.foo();
}
Subjectively, I also dislike the cpp
suffixes for template header files so much that I wanted to bring it to your attention. It is confusing and misleading, especially when one sees a cpp file included in another cpp file.
Consider using an ipp
or ixx
as the file extension coupled with hpp
and hxx
respectively, it's more clear that the file includes the definition/implementation of a particular template.
回答3:
Is our approach valid C++ (03 or 11) code or are we just lucky it worked?
Your usage is valid, but not canonical. The more common C++98 style would group the explicit instantiation definitions together with the implementation, in the point.cpp
file.
// point.h
template <int dim> struct Point {
...
void foo();
...
};
// point.cpp
#include "point.h"
template <int dim>
void Point<dim>::foo() {
...
}
// Now the implementation is available, so this is the time to use it:
template struct Point<2>;
template struct Point<3>;
With C++11, would we be able to achieve the same thing by including
point.cpp
inmain.cpp
and declaringextern template <int dim> struct Point;
?
You should never #include
a .cpp
file. The canonical style is to put the function template implementation in a header file, either directly in point.h
or in a template-implementation file named e.g. point.hpp
.
The benefit added by C++11 is that you can have fast compilation times and also use Point<4>
by implicit specialization. However, putting the implementation in a header with extern
specializations won't speed compilation Point<2>
and Point<3>
.
You can't literally say extern template <int dim> struct Point;
. The header needs to list all the specializations:
extern template struct Point<2>;
extern template struct Point<3>;
Delegating all specializations to somewhere else was the intended purpose of export
templates. This was a C++98 feature which proved to be too difficult to implement, and so it was removed from the C++11 standard.
来源:https://stackoverflow.com/questions/22575883/separating-definition-instantiation-of-template-classes-without-extern