Say I have my code structured this way:
header1.h
template <class T, template<class> class C>
struct metafunction {
using type = typename C<T>::type;
};
inline namespace msn {
template <class T> struct implementation;
}
// uses the *implementation* not defined in the header!
template <class T>
struct use_case {
using type = typename metafunction<T, implementation>::type;
};
cpp1.cpp
#include <header1.h>
// I'll only need this in this compilation unit, so the
// question is: "Is this a good place to define it?"
template <>
struct implementation<int> {
using type = int;
};
int main() {
using tt = int;
// is this point of instantiation OK due to
// the existence of a specialization in the same cpp file?
using tt = use_case<int>::type;
tt var;
(void)var;
}
My precondition is that I'll only use the specific specialization(s) inside the cpp file(s) so I won't have to deal with linker problems. I know this won't work for a cpp2.cpp
file including header1.h
and trying to just use use_case<int>
or redefining an implementation<int>
that violates the ODR. So what I'm asking is whether this code is analogous to its linear form (a version where everything is put into a single cpp file with a conforming order) that (apparently) compiles fine.
A template is not an actual class or function, but a "pattern" that the compiler uses to generate a family of classes or functions. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template.
This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.
To have all the information available, current compilers tend to require that a template must be fully defined whenever it is used. That includes all of its member functions and all template functions called from those. Consequently, template writers tend to place template definition in header files.
An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.
Yes, as long as this is used only within the same translation unit, this is well-formed.
Keep in mind that the effect of #include
is as-if the referenced file is inserted verbatim into the translation unit.
That's what an #include
is.
From this, we can draw several conclusions:
Every C++ translation unit is a single virtual file.
Therefore, every specialization in a well-formed translation unit is referenced in the same translation unit that defines it.
Q.E.D.
Generally, I agree with @Sam's answer, but I would do such a specialization only for local types. With "local type" I mean a type that is accessible only in this translation unit, e.g. a class that is defined only in this .cpp file (in an anonymous namespace).
The reason is that when specialization in this way on a type that is widely accessible, wider than the specialization, it's very easy to have an ODR (One Definition Rule) violation. If somewhen in the future another code starts using the template with the same type, it's easy to understand that you get two instantiations of the template which aren't the same (one with the specialization and one without it). In such a case, this is an ODR violation and the behavior isn't defined.
So int
it definitely not a good candidate for a "local" specialization.
If you still want to have the specialization locally in the .cpp file, maybe you want to add an additional tag to the template parameters (which requires adding another parameter to the original template too), and this tag will be with a local type (e.g. defining struct private_tag{};
inside an anonymous namespace in this file and using it in the specialization). This way, the specialization has a unique type that can't be used in any other TU, so you are safe from ODR violation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With