Template issue causes linker error (C++)

46,687

Solution 1

You have to have your template definitions available at the calling site. That means no .cpp files.

The reason is templates cannot be compiled. Think of functions as cookies, and the compiler is an oven.

Templates are only a cookie cutter, because they don't know what type of cookie they are. It only tells the compiler how to make the function when given a type, but in itself, it can't be used because there is no concrete type being operated on. You can't cook a cookie cutter. Only when you have the tasty cookie dough ready (i.e., given the compiler the dough [type])) can you cut the cookie and cook it.

Likewise, only when you actually use the template with a certain type can the compiler generate the actual function, and compile it. It can't do this, however, if the template definition is missing. You have to move it into the header file, so the caller of the function can make the cookie.

Solution 2

You are probably suffering from missing a valid instantiation. If you put your template definition in a separate .cpp file, when the compiler compiles that file it may not know which instantiations you need. Conversely, at the call sites which would instantiate the correct version of the template function, if the definition of the function body isn't available the compiler won't have the information to instantiate the required specializations.

You have two options. Put the function body for the function template in the header file.

e.g. in the header file:

template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

or explicitly instantiate the template in the .cpp where you've defined the template.

e.g. in the source file (will probably require #includeing the file that defines Item):

template <typename T>
T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

template Item* find_name<Item>(std::vector<Item*> v, std::string name);

Solution 3

The answers here are great.

I'll just add that this is often why in addition to .h and .cpp files in a project. You'll often find .inl files. The template definitions will go into the .inl file.

These .inl files mean inline and will usually be included by the .h file of the same name prefix at the bottom of the file after all the header declarations. This effectively makes them part of the header file but separates the declarations from any definitions.

Since they are glorified header files you should take all the same precautions that you would with a regular header file, ie include guards etc.

Solution 4

Stumbled upon the same issue and found this which states 3 workarounds: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp

Among them is an easy one where you create a "dummy" method in the .cpp file, that calls the template/class function with the different types. Pasted from the link:

// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
    TestTemp<int> TempObj;
    TestTemp<float> TempObj2;
}

Solution 5

I just noticed that you had a second question which seems to be unanswered:

Is there a way to make a template parameter so that it has to be a subclass of a certain class, i.e. template?

It is possible. For example, see is_base_of in Boost.TypeTraits.

However, I'm curious: Why do you want that? Normally, the requirements of a template on its parameters are not on the parameter's type itself, but on which expressions involving that type are legal. For example, imagine that you have:

template<class T>
void foo(const T& t)
{
    if (t.foo()){
       t.bar("blah");
    }
}

Saying that T must inherit from something like:

class HasFooAndBar
{
public:
  void foo()const;
  void bar(const char*)const;
};

brings nothing because the instantiation of the function will fail anyway if the type does not support the operations. Moreover, it needlessly restricts the applicability of foo(). In fact, foo's any requirements are that t.foo() and t.bar(const char*) are valid expressions on a const T. For example, this type does not inherit from HasFooAndBar and is still a valid foo() parameter:

struct DifferentFromHasFooAndBar
{
  bool foo()const;
  std::string bar(const std::string&)const;
};
Share:
46,687
marsolk
Author by

marsolk

Updated on July 08, 2022

Comments

  • marsolk
    marsolk almost 2 years

    I have very little idea what's going in regards to C++ templates, but I'm trying to implement a function that searches a vector for an element satisfying a given property (in this case, searching for one with the name given). My declaration in my .h file is as follows:

    template <typename T>
    T* find_name(std::vector<T*> v, std::string name);
    

    When I compile, I get this linker error when I call the function:

    Error   1   error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$find_name@VItem@@@@YAPAVItem@@V?$vector@PAVItem@@V?$allocator@PAVItem@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?get_item@Place@@QAEPAVItem@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) place.obj   Program2
    

    Again, I'm new to templates so I don't know what's going. All instances I've found of LNK2019 through Google have been about not using the correct libraries, but since this is my own function I don't see why this would be happening.

    Also, a related question: Is there a way to make a template parameter so that it has to be a subclass of a certain class, i.e. template?