15

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.

Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63

2 Answers2

8

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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
1

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.

Yehezkel B.
  • 1,140
  • 6
  • 10