views:

448

answers:

5

Hi, 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?

+10  A: 
GMan
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++.
tocapa
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
Function templates are *not* functions. Function templates are *templates*. They obey their own rules.
AndreyT
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
A: 

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

dirkgently
No, this isn't required. See Charles Bailey's answer above.
Jens Alfke
+2  A: 

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);
Charles Bailey
+1  A: 

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
A: 

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