68

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?

chollida
  • 7,834
  • 11
  • 55
  • 85
marsolk
  • 913
  • 1
  • 8
  • 10
  • what compiler are you using? some compilers prevent you from separating declaration and definition into separate files for templates. – Jordan Oct 28 '09 at 20:14
  • Did you actually write an implementation for your template function? – begray Oct 28 '09 at 20:14
  • 2
    You may also consider using std::find or std::find_if – Éric Malenfant Oct 28 '09 at 20:15
  • Passing v and name by value is costly. Pass them by const reference instead. – Éric Malenfant Oct 28 '09 at 20:16
  • You're right, Eric, I'm not very good w/ C++ yet. As for find, looks like something I should have looked harder for, but why does it return last if it doesn't find it? Shouldn't there be a better return value for a non-existent element? – marsolk Oct 28 '09 at 20:36
  • nvm I really need to read up on C++ – marsolk Oct 28 '09 at 20:37
  • About returning last if the value is not found: In fact, it does not return the "last", it returns an iterator denoting the end of the range, which "points" one *past* the end. – Éric Malenfant Oct 28 '09 at 20:48
  • I think that here you can find other interesting explanation about this topic: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file . – bitfox Jan 08 '20 at 12:37

6 Answers6

107

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);
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
97

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.

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Thank you. I now understand that, but I'm afraid I don't quite understand why it can do this for regular functions but not for template functions, which I suppose I can chalk up to not a full understanding of C++. – marsolk Oct 28 '09 at 20:17
  • Templates are boilerplates, somewhat like macros, just better (and worse ;). Until and unless you use them, the compiler is not required to do a macro-like replacement with the given type(s) and create the actual function. Now, when the compiler hits this instantiation, it may or it may not have the boilerplate in hand. If it doesn't you hit a roadblock. – dirkgently Oct 28 '09 at 20:20
  • 2
    Function templates are *not* functions. Function templates are *templates*. They obey their own rules. – AnT stands with Russia Oct 28 '09 at 20:20
  • 7
    It's not correct to say that you can't put template definitions into .cpp files. See Charles Bailey's answer below for a more complete explanation of the issue. – Jens Alfke Nov 13 '09 at 17:45
  • I initially put the definitions on cpp to break circular references, having the two classes in one .h file. I end up copying the definitions of functions of the first class after the second class. – AndrewBloom Feb 21 '21 at 01:32
18

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.

chollida
  • 7,834
  • 11
  • 55
  • 85
6

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;
}
KBog
  • 3,800
  • 1
  • 25
  • 31
  • 5
    I really like this method, but is there a way to make sure compiler will not optimise this thing out? I once tried -O3 optimization, and then `undefined symbol` – darwinsenior Mar 22 '16 at 20:47
1

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;
};
Éric Malenfant
  • 13,938
  • 1
  • 40
  • 42
0

Did you put your template function definition in a cpp file? Then move it to the header and inline it.

dirkgently
  • 108,024
  • 16
  • 131
  • 187