There's an area of C++ which is surprisingly little known and little understood even by many expert C++ programmers. I have heard of people who have programmed in C++ professionally for over a decade, and they are quite fluent at template programming, yet have surprisingly little knowledge about this subject. More precisely: Template declarations.
In order to clear any possible confusion, let me first explain what "declaration", as opposed to "definition", means in C++ (and some other languages).
A declaration simply tells that a function or a class with a certain name and signature exists somewhere, but without specifying its implementation. For example, this is a function declaration, very typical in C and C++ code:
int someFunction(double, double);
What that line says is that there exists a function named
someFunction
which takes two doubles as parameter and returns
an int. However, the implementation of that function may be somewhere else,
in a completely different compilation unit. (Basically this is an instruction
for the compiler that says that whenever this function is called, for the
compiler to generate instructions for the linker to put a proper function
call in that place.)
The definition of the function would be its implementation. A function can be declared many times in many compilation units, but it must be defined (ie. implemented) only once in one compilation unit (or else the linker will give an error about multiple definitions).
Class declarations vs. definitions is a bit more complicated subject, as there are basically three stages:
class SomeClass;
This is also called an "incomplete type" (eg. by the C++ standard). It is called so because such declaration does not have enough information for the code to create instances of the class nor call any of its member functions. (The only thing the code can do with an incomplete type is to pass pointers and references of that type around.)
class SomeClass { public: void someMemberFunction(int); };
This is a class definition in the sense that it allows the
code to create instances of the class and call its member functions.
However, the member function someMemberFunction()
has
only been declared rather than defined (which like with regular function
declarations means that its implementation is somewhere else).
Quite many C++ programmers, even those who are quite fluent at template programming, have the mistaken notion that template functions and classes can only be defined, not declared. In other words, whenever you write a template function, you must give its implementation right there, and likewise for a class. For example, that you must write code like:
template<typename T> T min(T v1, T v2) { return v1 < v2 ? v1 : v2; }
It's surprisingly little known that you can, in fact, write templated function declarations instead. In other words, the following is completely valid:
template<typename T> T min(T v1, T v2);
Many C++ programmers are surprised that the above even compiles rather than giving an error message outright. (It will give a linker error if the function has not been defined and instantiated somewhere, but the declaration above is completely valid.)
Basically what the code above does is that when it's called, the compiler
will create a declaration of the min()
function with
the specified template parameter type. It's then up to the linker to try
to find a definition of this specific function somewhere (which obviously
must be provided by the programmer somehow, or else a linker error will be
issued, naturally).
Likewise templated class member functions can be declared, without a definition.
Even less known is the fact that template classes can be declared, with no implementation. You can, in fact, write something like:
template<typename T> class SomeClass;
Since the type T
is not actually being used in this
declaration for anything, it can be omitted in the same way as function
parameter names can be omitted when they are not used (another detail
surprisingly few C++ programmers know). In other words, the above is
completely equivalent to:
template<typename> class SomeClass;
If SomeClass
is referred to ("instantiated" so to speak) with
a type, the compiler will then create a class declaration (rather than a
definition) of that class with the specified template type. This declaration
will work in the exact same way as regular (non-template) class declarations.
One practical situation where templated class declarations can be used (again a "trick" very little known even by expert C++ programmers) is to declare friend classes, when those classes are template classes. Few C++ programmers know how to do that, but it's rather simple, really. All that one has to say is:
class A { template<typename> friend class B; ... };
What the above code is saying is that if there's a template class
B
(which takes one template type parameter), it's a friend of
class A
(whatever that template parameter might be). Whenever
any class of type B
does anything to an object of type
A
, the template declaration above will kick in and make that
B
a friend class of A
(regardless of which template
parameter type B
was instantiated with).
(We could give the template parameter in the friend class declaration above a name, but as commented earlier, since this name is not used for anything, we can simply omit it.)
A practical situation where one wants to do the above is when dealing with intrusive smart pointers (or other similar constructs).
An intrusive smart pointer requires for a reference count to exist in the object being managed, and for a way to increment and decrement that count. Since it would be needlessly laborious (and bad object-oriented design) to add such members to all the classes being managed, a common solution is to create a base class from which all classes with a reference counter inherit. In other words, when you write a class which will be managed with such an intrusive smart pointer, you do it like:
class MyClass: public ReferenceCountable { ... };
The ReferenceCountable
base class (which contains a reference
count member variable) is provided by the same library that provides the
intrusive smart pointer itself.
Of course there's a design conundrum with respect to
ReferenceCountable
: How should the intrusive smart pointer be
able to access the reference count? Should the reference count be a public
member? That would be quite ugly design-wise. Should there be some public
member functions to increment and decrement the counter? Slightly better,
but it's still a dubious design because the reference counter is basically
a private detail of the intrusive smart pointer library, yet the public
functions allow for outside code to tamper with the count variable.
The optimal solution would be, naturally, for the reference count to be
a private member of the ReferenceCountable
class, and for the
intrusive smart pointer to be the only other class allowed to access it.
This can be done with a friend
statement. However, since the
intrusive smart pointer is a template class by necessity, most C++ programmers
don't know how to do that. However, as stated, it's rather simple (let's
assume that the smart pointer class is named "IntrusivePtr
"):
class ReferenceCountable { template<typename> friend class IntrusivePtr; size_t referenceCount; public: ReferenceCountable(): referenceCount(1) {} ReferenceCountable(const ReferenceCountable&) {} ReferenceCountable& operator=(const ReferenceCountable&) { return *this; } };
Now the template class IntrusivePtr
will be able to access
the private reference count, but no other classes can.
(Note the empty copy constructor and assignment operator implementations. This is not a mistake nor an error, but in fact such a base class must be implemented like that. I'll leave it as an exercise to the reader to figure out why. Suffice to say that approximately 99% of intrusive smart pointer implementations out there, even ones made by expert C++ programmers, omit these, causing malfunction in certain situations.)
The C++98 standard defines so-called "export templates", which would allow template functions and template class member functions to simply be declared, while having their implementation in a separate compilation unit, which would then be automatically instantiated by the linker for each used type. Because compilers have not implemented this feature (even though it would be quite useful) as it's deemed way too laborious to implement, the next C++ standard will deprecate the feature.
Not all is lost, though. Export templates can still be "emulated" in a slightly more limited way by making use of template declarations, as described earlier. As commented in the beginning, template functions can be declared and this will not cause any error as long as the function is then defined and instantiated somewhere where the linker can find it.
So let's take the earlier example of the min()
function which
was only declared but not defined (eg. in a header file):
template<typename T> T min(T v1, T v2);
Once we have made such a declaration, we can call that function from our code. It's only at the linking stage that the linker will complain that it cannot find the definition. But we can provide such a definition somewhere, and in fact, do so in a way which "emulates" pretty much what export templates would do (even though there are limitations).
What we can do is to write the implementation of the above function in
a compilation unit and then use explicit template instantiation
to create an instance of that function. The explicit instantiation has to
be done for each type that is being used with the function. For example, if
the min()
function above is being called with the types
int
and double
, we would do the following (in
some source file somewhere):
template<typename T> T min(T v1, T v2) { return v1 < v2 ? v1 : v2; } template int min(int, int); template double min(double, double);
Explicit template instantiation is a special syntax that C++
provides to explicitly instantiate a template function or class with a
certain type. In the most basic form this happens simply by writing the
keyword 'template
' followed by the function declaration with
the desired type substituted for the template parameters, as done above.
If a template function doesn't return nor take any parameter of the template type, explicit instantiation can still be done with the syntax:
template void someTemplateFunction<int>();
The same idea works when explicitly instantiating template classes:
template class SomeTemplateClass<int>;
What the min()
example above does is that it first defines
the function itself, and then explicitly instantiates it with the types
int
and double
. If these were the two types which
were used when calling the function somewhere else, the linker will then
find these instantiations, and the linking will succeed.
As stated, this feature can be used to "emulate" export templates. Basically the only difference (and drawback) is that the template has to always be manually instantiated for each used type. There's no way to automatize this (which is what export templates would do, if supported by the compiler).
This can be done with individual template functions, individual member functions of a template class, and even entire template classes.