The standard (from working draft of C++0x through working draft in 2014), section 14.7.2 describes Explicit instantiation, stating that there are two forms of explicit instantiation, definition and declaration. It says, "an explicit instantiation declaration begins with the extern keyword." It further specifies that declarations, using extern, do not generate code.
Care must be taken to ensure the declarations are issued within the namespace of the template class declaration, or specifically reference the namespace in the qualified name, as in:
namespace N {
template< class T > void f( T& ) {}
}
template void N::f<int>(int &);
Instantiating a template function, and generating it's code (definition). Whereas:
extern template void N::f<int>(int &);
Instantiates a template function for type int as a declaration, but does not generate code. The extern keyword informs the compiler that the code will be provided at link time from another source (possibly a dynamic library, but the standard doesn't discuss that platform specific concept).
Further, it is possible to instantiate members and member functions selectively, as in:
namespace N {
template<class T> class Y { void mf() { } };
}
template void N::Y<double>::mf();
This generates the code only for the function mf(), for doubles. It is, therefore, possible to declare instantiations (using extern), and then define instantations (without extern) for specific parts of a template type. One could elect to generate code or members for some portions of a template class within every compilation unit (inline), and force the generation of other portions of code into a particular compilation unit or library.
IBM's Knowledge Center article for their XLC V 11.1 compiler, supporting draft C++0x, discusses the strategy for using the extern keyword when building libraries. From their example, and the draft standards documents over several years (which have been consistent from 2008 forward on this subject), it's clear that extern has limited applicability to the specifics of dynamic libraries, but in general is limited to controlling where generated code is placed. The author will still be required to adhere to platform specific demands relative to dynamic linking (and loading). That is beyond the purpose of the extern keyword.
Extern is equally applicable to static libraries or dynamic libraries, but the limitation on a library design is significant.
Say a template class declaration, presented in a header file, exists like:
namespace N
{
template< typename T >
class Y
{
private:
int x;
T v;
public:
void f1( T & );
void f2( T &, int );
};
}
Next, in a CPP file:
namespace N
{
template< typename T> void Y<T>::f1( T & ) { .... }
template< typename T> void Y<T>::f2( T &, int ) { .... }
}
Now consider the potential uses of Y. Consumers of the library may only require instantiations of Y for int, float and double. All other uses would be of no value. This is a design point of the author of the library, not some general notion about this concept. For whatever reason, the author only supports those three types for T.
To that end, explicit instantiation declarations may be included in the header file
extern template class N::Y< int >;
extern template class N::Y< float >;
extern template class N::Y< double >;
As this is processed by the user's various compilation units, the compiler is informed there will be code generated for these three types, but the code is not generated in each compilation unit as the user builds. Indeed, if the author doesn't include the CPP file defining the functions f1 and f2 for template class Y, the user wouldn't be able to use the libary.
Assuming, for the moment, a static library is the intended product regarding template class Y (for simplification of this discussion), the author compiles the static library with the CPP defining functions f1 and f2, along with the explicit instantiation definitions:
template class N::Y< int >;
template class N::Y< float >;
template class N::Y< double >;
This will cause the code to be generated for template class Y on behalf of these three types, creating a static library. User code will now have to link to this library, but do nothing else to use the classes. Their compilation units will not generat code for template class Y, instead incorporating that code from the library.
The same concept applies to a dynamic library, but platform specifics regarding function declarations, dynamic loading and dynamic linking are not found in the standards for C++ through 2014 working drafts, regarding C++0x, C++11 or C++14, at present. The extern keyword in explicit template instantiations is limited to creating declarations, it's absence creates definitions (where code is generated).
This brings up the question regarding users of such a library intent on using Y for unsigned long, char, or some other type not provided in a dynamic or static library. The author has the choice of refusing to support this by not distributing source for the generation of code (the function definitions of f1 and f2 for template class Y). However, if the author did wish to support such usage, distributing that source, instructions would be required for the user to generate a new library to replace the existing one, or generating a second library for the additional types.
For either case, it would be wise to separate the explicit instantiation definitions in a CPP file which includes the header declaring template class Y, the including a header of the funtion definitions of f1 and f2 for template class Y (as opposed to the practice of including a CPP file, which could also work). In this way, the user would create a CPP file that includes the header for template class Y, then the function definitions for template class Y, then issuing the new explicit instantiation definitions:
#include "ydeclaration.h" // the declaration of template class Y
#include "ydefinition.h" // the definition of template class Y functions (like a CPP)
template class N::Y< unsigned long >;
template class N::Y< char >;
For a static library, little else would be required, and the user could opt to build the additional compilation unit within their project, obviating the need for a static library target.
However, if the user wanted to build a dynamic library, care would be required regarding the platform specific code regarding dynamic libraries on a particular platform. Specifically on Windows, for example, this may mean explicitly loading the new dynamic library.
Considering the complications involved with creating dynamic libraries, it is a wonder anyone ever does. Sometimes there simply is no other choice. Key to the decision is to determine exactly why a dynamic library should be used. In the ancient epoch of computers with under 1 GByte of RAM, one of the justifications was saving memory by sharing code, but for any particular library, what is the probability that code sharing would result in saving RAM? For something as common as the C runtime, or Windows MFC DLL's, it may be highly probable. Libraries which provide highly targeted services, on the other hand, are more likely to be used by only one running program.
One really good purpose is the concept of plug in behaviors. Browsers, IDE's, photo editing software, CAD software and others benefit from an entire industry of applications distributed as plugins to existing products, which are distributed as dynamic libraries.
Another justification is distributing updates. While this is an attractive theory, the practice can cause more problems than it's worth.
Another common justification is "modularity". To what end, though? Separating compilation units already reduces compile times. Dynamic libraries will affect link times more than compile times, but is that worth the additional complexity?
Otherwise, however, providing dynamic libraries, especially for a fairly small product, is not really worth the trouble.
An entire book could be written on the subject of writing portable dynamic libraries applicable to both Windows and Linux.
On Windows, the choice of using __declspec(dllexport/dllimport) can apply to an entire class. It's important to realize, however, that whatever compiler is used to generate the DLL can only be used with targets built with that same compiler, or compatible compilers. Within the MS VC lineage, many versions are NOT compatible with each other at this level, so a DLL built with one version of Visual Studio may not be compatible with other versions, causing a burden on the author to generate DLL's for every possible compiler/version to be supported.
There are similar issues regarding static libraries. Client code must link with the same version and configuration of the CRT as the DLL is built with (is the CRT statically linked?). Client code must also elect the same exception handling settings and RTTI settings as the library is built with.
When portability to Linux or UNIX (or Android/iOS) is to be considered, the problems magnify. Dynamic linking is a platform specific concept not handled in C++.
Static libraries would probably be the best approach, and for those __declspec(dllexport/dllimport) should not be used.
With all that said against dynamic libraries, here's one of the many ways to implement that in Windows (completely inapplicable to Linux/UNIX/etc).
A simple (perhaps naive but convenient) approach is to delcare the entire class as exported from a DLL (imported in client code). This has the slight advantage over declaring each function as exported or imported, because this approach includes class data, and just as importantly, AUTOMATIC assignment/destructor/constructor code C++ may build for your class. That could be vitally important if you didn't carefully attend to those and export them manually.
In a header to be included for the DLL production:
#define DLL_EXPORT // or something similar, to indicate the DLL is being built
That will be included at the top of your header declaring your library's template classes. The header declaring DLL_EXPORT is only used in a project configured to compile the DLL library. All client code will import an otherwise empty version. (Myriad Other methods for doing this exist).
As such, DLL_EXPORT is defined when building for the DLL, not defined when building client code.
In the header for your library's template class declarations:
#ifdef _WIN32 // any Windows compliant compiler, might use _MSC_VER for VC specific code
#ifdef DLL_EXPORT
#define LIB_DECL __declspec(dllexport)
#else
#define LIB_DECL __declspec(dllimport)
#endif
Or whatever you prefer to see in place of LIB_DECL as a means of declaring entire classes exportable from DLL's, imported in client code.
Proceed with class declarations as:
namespace N
{
template< typename T >
struct LIB_DECL Y
{
int x;
T v;
std::vector< T > VecOfT;
void f1( T & );
void f2( T &, int );
};
}
Explicit instantiation declarations for this would be:
extern template struct LIB_DECL N::Y< int >;
extern template struct LIB_DECL N::Y< float >;
extern template struct LIB_DECL N::Y< double >;
extern template class LIB_DECL std::vector< int >;
extern template class LIB_DECL std::vector< float >;
extern template class LIB_DECL std::vector< double >;
Take note of the std::vector used in class Y in this example. Consider the problem carefully. If your DLL library uses std::vector (or any STL class, this is just an example), the implementation you used at the time you build the DLL must match what the user chooses when they build client code. The 3 explicit instantiations of vector match the requirements of the template class Y, and instantiate std::vector within the DLL, and with this declaration become exportable from the DLL.
Consider, how would the DLL code USE std::vector. What would generate the code in the DLL? It's obvious from experience that the source for std::vector is inline - it's a header only file. If your DLL does instantiate vector's code, how would client code access it? Client code would "see" std::vector source and attempt it's own inline generation of that code where the client access std::vector functions. If, and only if, the two are an exact match would that work. Any difference between the source used to build the DLL and the source used to build the client would differ. If client code had access to the std::vector in template class T, there would be chaos if the client used a different version or implementation (or had different compiler settings) when using std::vector.
You have the option of explicitly generating std::vector, and informing client code to use THAT generated code by declaring std::vector as an extern template class, to be imported in client code (exported in DLL builds).
Now, in the CPP where the DLL is built - the function definitions of the library - you must explicitly instantiate definitions:
template struct LIB_DECL N::Y< int >;
template struct LIB_DECL N::Y< float >;
template struct LIB_DECL N::Y< double >;
template class LIB_DECL std::vector< int >;
template class LIB_DECL std::vector< float >;
template class LIB_DECL std::vector< double >;
In some examples, like MS KB 168958, they suggest making the extern keyword a define, modifying this plan as:
#ifdef _WIN32 // any Windows compliant compiler, might use _MSC_VER for VC specific code
#ifdef DLL_EXPORT
#define LIB_DECL __declspec(dllexport)
#define EX_TEMPLATE
#else
#define LIB_DECL __declspec(dllimport)
#define EX_TEMPLATE extern
#endif
So that in the header file for both DLL and client builds, you may simply state
EX_TEMPLATE template struct LIB_DECL N::Y< int >;
EX_TEMPLATE template struct LIB_DECL N::Y< float >;
EX_TEMPLATE template struct LIB_DECL N::Y< double >;
EX_TEMPLATE template class LIB_DECL std::vector< int >;
EX_TEMPLATE template class LIB_DECL std::vector< float >;
EX_TEMPLATE template class LIB_DECL std::vector< double >;
While this has the advantage of issuing these lines once, in the header, I personally prefer observing the extern keyword clearly in use in the header, so that I know without a doubt the only place code generation can take place is in the CPP of the DLL build (where they appear, a second time, without the extern keyword). In this way, the extern's in the header are forward declarations, which do not conflict with the explicit instantiation definitions in the CPP, and it avoids obfuscating the extern keyword when used in the client code. It is, perhaps, a peculiar preference of my own.
You may be thinking, "what about other client code and std::vector". Well, it's important to consider. Your header file includes std::vector, but remember, your DLL is built with the code available to YOU at compile time. Your client will have their own header, it within like versions of VC, that should be the same. SHOULD is not a good plan, though. It could be different. They may just assume VC 2015 is the same and plow ahead. Any difference, be it object layout, actual code..anything, could doom the running application with very strange effects. If you export your version, they client would be well advised to include the explicit instantiation declarations throughout all their compilation units, so everything uses YOUR version of std::vector...but, there's a serious catch.
What if some other library did this, too, with yet a different version of std::vector?
It makes the use of STL a bit nasty in these contexts, so there's one fairly good design choice that eliminates this. Don't expose any use of STL.
If you keep all use of STL private to your library, and never expose an STL container to client code, you're probably in the clear. If you choose that in design, you have no need to explicitly instantiate the std::vector (or any STL) in your library.
I included the example so as to discuss it, how it's documented by MS (KB 168958), and why you probably don't want to do it. However, the reverse scenario comes up, too.
In the original inquiry, the use of std::string (or one of it's alternatives) is to be used. Think of this: in the DLL, how will that use of std::string instantiate? What if there is any difference between the std::string code available when the DLL was built compared to that which is used by the client whey they build? The client could, after all, elect to use some other STL than provided by MS. Of course, you could stipulate they not do that, but...perhaps you could explicitly instantiate std::string as extern WITHIN your DLL. In that way, you have no STL code built within the DLL, and the compiler is now informed that it should find that code built by the client, not within the DLL. I suggest it for research and thought.
The insidious problem you face is this: this will all work on your machine, in your tests, because you're using one compiler. It would be flawless there, but could fail spectacularly in client builds because of code differences, or setting differences, subtle enough to escape warnings.
So, let's assume you agree and skip the last three lines in the examples which instantiate std::vector...is it done?
That depends on your IDE settings, which I'll leave to you. The question centered around the use of __declspec(dllxxxxx) and it's usage, and there are several ways to implement its use, I focused on one. Whether or not you must explicitly load the library, rely on automatic dynamic linking features, consider the DLL_PATH...these are general DLL building topics you either know, or are beyond the real scope of the question.