Crashworks
Crashworks

Reputation: 41444

"Unresolved external symbol" for templated function declared inline in header

I have a Visual Studio 2012 project equivalent to this:

Header.h

template< class T >
inline int Demonstrate( const char *txt, T *input )
{
  return printf("%s %d %f\n", txt, input->Integer(), input->Real() );
}

Source.cpp

#include "Header.h"

class Foo 
{
public:
   int Integer() { return 2; }
   float Real() { return 3.14159f; }
};

int main()
{
   Foo example;
   printf( "%d\n", Demonstrate( "foo:", &example ) );
   return 0;
}

Yet when I compile I receive a LNK2019 error:

unresolved external symbol "int __cdecl Demonstrate(char const *,class Foo *)"

Ordinarily this occurs when a templated function is declared in a header but only defined in a cpp, but that is not the case here. The function is defined inline in the header.

What could cause this and how can I fix it?

edit

This happens even if I remove the header altogether and just stick Demonstrate() at the top of Source.cpp. It happens whether "Inline Function Expansion" in the project properties is set to "Default" or to "/Ob2" . This must be some project settings thing, but what?

Upvotes: 3

Views: 701

Answers (2)

Crashworks
Crashworks

Reputation: 41444

So I tracked this down and it turns out that Joel was on the right path. The function Demonstrate() had been prototyped multiple times in multiple headers — in a very nonobvious way. There was the explicit int Demonstrate( const char *txt, Foo *input ) declaration, which is what I replaced with a template.

But there were several other headers that had, strewn across them, something analogous to this (you can infer that the actual function and class names were much more complicated):

header a.h:

#define FUNC_PREFIX Demo

header b.h:

#define REGISTER_CLASS( retype, classname, FUNC_SUFFIX ) retype FUNC_PREFIX ## FUNC_SUFFIX ( const char *txt, classname *ptr )

header c.h:

REGISTER_CLASS( int, Foo, nstrate );
REGISTER_CLASS( int, Bar, nstrate );
// etc

I'm not sure what to draw from this. On the one hand it's a very specific bug to a very specific codebase and too localized to be a useful SO question. On the other hand, there is a teachable moment here:

DON'T USE MACROS TO DEFINE GLOBAL FUNCTIONS IN SNEAKY WAYS.

Or else poor saps like me will spend hours tracking down problems like this.

Upvotes: 2

paddy
paddy

Reputation: 63481

Sometimes you can't rely on automatic template specialization. I've encountered this before with VS and ended up having to be explicit. ie

Demonstrate<Foo>("foo:", &example)

Upvotes: 1

Related Questions