Visibility of template specialization of C++ funct

2019-02-11 11:33发布

问题:

Suppose I have fileA.h which declares a class classA with template function SomeFunc<T>(). This function is implemented directly in the header file (as is usual for template functions). Now I add a specialized implementation of SomeFunc() (like for SomeFunc<int>()) in fileA.C (ie. not in the header file).

If I now call SomeFunc<int>() from some other code (maybe also from another library), would it call the generic version, or the specialization?

I have this problem right now, where the class and function live in a library which is used by two applications. And one application correctly uses the specialization, while another app uses the generic form (which causes runtime problems later on). Why the difference? Could this be related to linker options etc? This is on Linux, with g++ 4.1.2.

回答1:

It is an error to have a specialization for a template which is not visible at the point of call. Unfortunately, compilers are not required to diagnose this error, and can then do what they like with your code (in standardese it is "ill formed, no diagnostic required").

Technically, you need to define the specialization in the header file, but just about every compiler will handle this as you might expect: this is fixed in C++11 with the new "extern template" facility:

extern template<> SomeFunc<int>();

This explicitly declares that the particular specialization is defined elsewhere. Many compilers support this already, some with and some without the extern.



回答2:

Have you added a prototype with parameters to your header file?

I mean is there somewhere in fileA.h

template<> SomeFunc<int>();

If not that's probably the reason.



回答3:

I had the same problem with gcc4, here is how i solved it. It was more simple a solution than what i was lead to believe by previous comments. The previous posts ideas were correct but their syntax didn't work for me.


    ----------header-----------------
    template < class A >
    void foobar(A& object)
    {
      std::cout << object;
    }

    template <> 
    void foobar(int);

    ---------source------------------
    #include "header.hpp"

    template <>
    void foobar(int x)
    {
      std::cout << "an int";
    }



回答4:

Per the specs, your specialized function template should never be called outside fileA.C, unless you export the template definition, which no compiler (except Comeau) currently supports (or has it planned for the forseeable future).

On the other hand, once the function template is instantiated, there is a function visible to the compiler that is no longer a template. GCC may re-use this definition across different compiler units because the standard states that each template shall only be instantiated once for a given set of type arguments [temp.spec]. Still, since the template is not exported, this should be limited to the compilation unit.

I believe that GCC may expose a bug here in sharing its list of instantiated templates across compilation units. Normally, this is a reasonable optimization but it should take function specializations into account which it doesn't seem to do correctly.



回答5:

As Anthony Williams says, the extern template construct is the correct way to do this, but since his sample code is incomplete and has multiple syntax errors, here's a complete solution.

fileA.h:

namespace myNamespace {
  class classA {
    public:
      template <class T> void SomeFunc() { ... }
  };

  // The following line declares the specialization SomeFunc<int>().
  template <> void classA::SomeFunc<int>();

  // The following line externalizes the instantiation of the previously
  // declared specialization SomeFunc<int>(). If the preceding line is omitted,
  // the following line PREVENTS the specialization of SomeFunc<int>();
  // SomeFunc<int>() will not be usable unless it is manually instantiated
  // separately). When the preceding line is included, all the compilers I
  // tested this on, including gcc, behave exactly the same (throwing a link
  // error if the specialization of SomeFunc<int>() is not instantiated
  // separately), regardless of whether or not the following line is included;
  // however, my understanding is that nothing in the standard requires that
  // behavior if the following line is NOT included.
  extern template void classA::SomeFunc<int>();
}

fileA.C:

#include "fileA.h"

template <> void myNamespace::classA::SomeFunc<int>() { ... }


回答6:

Brandon: that's what I thought - the specialized function should never be called. Which is true for the second application I mentioned. The first app, however, clearly calls the specialized form even though the specialization is not declared in the header file!

I mainly seek enlightenment here :-) because the first app is a unit test, and it's unfortunate to have a bug that doesn't appear in the test but in the real app...

(PS: I have fixed this specific bug, indeed by declaring the specialization in the header; but what other similar bugs might still be hidden?)



回答7:

In Microsoft C++, I did an experiment with inline functions. I wanted to know what would happen if I defined incompatible versions of a function in different sources. I got different results depending on whether I was using a Debug build or a Release build. In Debug, the compiler refuses to inline anything, and the linker was linking the same version of the function no matter what was in scope in the source. In Release, the compiler inlined whichever version had been defined at the time, and you got differing versions of the function.

In neither case were there any warnings. I kind of suspected this, which is why I did the experiment.

I assume that template functions would behave the same, as would other compilers.



回答8:

@[anthony-williams],

are you sure you're not confusing extern template declarations with extern template instantiations? From what I see, extern template may only be used for explicit instantiation, not for specialization (which implies implicit instantiation). [temp.expl.spec] doesn't mention the extern keyword:

explicit-specialization:
    template < > declaration



回答9:

Unless the specialized template function is also listed in the header file, the other application will have no knowledge of the specialized version. The solution is the add SomeFunc<int>() to the header as well.