views:

99

answers:

4

I have something like this in my code:

template <typename T>
struct A
{
  void Print();
};

template <>
struct A<char*>
{
  void Print() { printf("Char*!\n"); }
};

template <typename T>
void DoSomething(T& lol)
{
  A<T> a;
  a.Print();
}

int main()
{
  char a[5];
  DoSomething(a);
}

And this produces the following linker error:

error LNK2019: unresolved external symbol "public: void __thiscall A<char [5]>::Print(void)" (?Print@?$A@$$BY04D@@QAEXXZ) referenced in function "void __cdecl DoSomething<char [5]>(char const (&)[5])" (??$DoSomething@$$BY04D@@YAXAAY04$$CBD@Z)

What type should I specialize the A template for, so that I can use it with a array-of-char? I tried const char* and other combinations of const, char, * and &, and nothing works.

Note that I cannot change the DoSomething function.

Also, if possible, I would like the compiler to automatically deduce (or convert) the template type without specifying it in the DoSomething<smth>() call in main().

+4  A: 

a does not have type char*, it has type char[5], so the primary template is instantiated, not the specialization.

If you manually perform the array-to-pointer conversion, it will use the specialization:

char a[5];
char* aptr = a;
DoSomething(a);

If you don't want the primary template to be used for a char array, you can specialize the template:

template <unsigned N> struct A<char[N]> { /* ... */ };
James McNellis
What if I can't (or really, really do not want to) perform the array-to-pointer conversion?
Kronikarz
@Kronikarz: Then you need to specialize the template for an array type.
James McNellis
A: 
char a[5];
char* aPointer = &a[0];
DoSomething(aPointer);

This will pass a char* to DoSomething.

Here is your complete sample code, modified to do this correctly:

template <typename T>
struct A
{
  void Print();
};

template <>
struct A<char*>
{
  void Print() { printf("Char*!\n"); }
};

template <typename T>
void DoSomething(T& lol)
{
  A<T> a;
  a.Print();
}

int main()
{
  char a[5];
  char* aPointer = &a[0];
  DoSomething(aPointer);
}
Merlyn Morgan-Graham
+1  A: 

You could just follow the compile error and specialize for char[5].

template <>
struct A<char*>
{
  void Print() { printf("Char*!\n"); }
};

It seems like a very strange specialization to provide, though.

If you want to explicitly specify a which specialization of DoSomething to use, then why not do exactly that?

int main()
{
  char a[5];
  DoSomething<char *>(a);
}

Of course, you'll find that this still doesn't compile as DoSomething takes a non-const reference so you need an lvalue of type char *, a temporary won't do.

int main()
{
  char a[5];
  char *p = a;
  DoSomething<char *>(p);
}
Charles Bailey
A: 

Another possibility to force array->pointer decay would be creating a specialization of DoSomething:

template <typename T>
void DoSomething(const T& lol)
{
  A<T> a;
  a.Print();
}

template <class T, unsigned N>
void DoSomething(T(& x)[N])
{
  DoSomething(x+0);
}

(you should make the parameter of DoSomething const since nonconst reference cannot work as a reference if you pass arrays to it.

jpalecek
Charles Bailey