progician
progician

Reputation: 920

Template specialization fails on linking

I have an issue with template specialization which I would like to understand. I'm working with Visual C++ 10.0 (2010). I have a class like this:

class VariableManager
{
public:

    template<typename VarT>
        VarT get(std::string const& name) const
        {
                    // Some code...
        }

   // This method supposed to be fully evaluated, linkable method.   
    template<>
        std::string get<std::string>(std::string const& name) const;

private:
    std::map<std::string, boost::any> mVariables;
};

In theory, because I specialized the "get" method, the linker should be able to pick up from an object file. Instead, I get an unresolved reference error with the linker if I place the method in the source file:

    template<>
    std::string VariableManager::get<std::string>(std::string const& name) const
            {
                 // Doing something...
            }

If I place this method in the header file as inline, the build goes just fine. I do understand that template functions as this:

        template<typename VarT>
            VarT get(std::string const& name) const;

should be placed in the header because the compiler won't be able to specialize the template according to the calling code, but in the case of full specialization it is the class' implementation that does that thus the specialized template method should already exist as a public symbol. Could somebody throw some light on this issue?

Upvotes: 9

Views: 3078

Answers (3)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506877

Your analysis is correct - an explicitly specialized function template that has any template parameters specified with explicit values provides a complete definition of a function.

If you have properly included the corresponding .cpp file that contains the explicit specialization's definition into your project, then VC++ should not raise a linker error. For Standards compliance though, let me note that you have to declare your specialization outside of the enclosing class. The Standard forbids to declare explicit specializations inside of the enclosing class (and other compilers will reject your code). So change the header file to declare the specialization like this, instead

class VariableManager
{
public:

    template<typename VarT>
        VarT get(std::string const& name) const
        {
                    // Some code...
        }

private:
    std::map<std::string, boost::any> mVariables;
};

// This method supposed to be fully evaluated, linkable method.   
template<>
std::string VariableManager::get<std::string>(std::string const& name) const;

Let me also note that you can't call get<std::string> inside of your class body. That's because any such call would not yet see the explicit specialization declaration, and would hence try to instantiate the function from the template definition. The Standard renders such code ill-formed, without a diagnostic being required.

Upvotes: 11

iammilind
iammilind

Reputation: 69988

Method starting with template<> is still considered a template specialization method. So you must have to put into a header file.

If you want to put it into a implementation file then you have to overload it.

class VariableManager
{
//...
  VarT get(std::string const& name) const
  {}

  std::string get(std::string const& name) const; //overloading not specialization
};

Upvotes: 0

reko_t
reko_t

Reputation: 56430

Specializing a template doesn't force the compiler to instantiate it (I think that GCC does though); you still need to explicitly tell the compiler to actually instantiate the template. You can do so with explicit template instantiation. Basically, just add this in the source file:

template std::string VariableManager::get(const std::string& name) const;

Upvotes: 0

Related Questions