«неразрешенный внешний символ» в специализации шаблона для массива символов - PullRequest
1 голос
/ 08 августа 2010

У меня есть что-то вроде этого в моем коде:

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);
}

И это приводит к следующей ошибке компоновщика:

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)

Для какого типа я должен специализировать шаблон A, чтобы я мог использовать его с массивом символов? Я пробовал const char* и другие комбинации const, char, * и &, и ничего не работает.

Обратите внимание, что я не могу изменить функцию DoSomething.

Также, если возможно, я бы хотел, чтобы компилятор автоматически выводил (или преобразовывал) тип шаблона, не указывая его при вызове DoSomething<smth>() в main().

Ответы [ 4 ]

5 голосов
/ 08 августа 2010

a не имеет типа char*, имеет тип char[5], поэтому создается экземпляр первичного шаблона, а не специализация.

Если вы вручную выполняете преобразование массива в указатель,он будет использовать специализацию:

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

Если вы не хотите, чтобы основной массив использовался для массива char, вы можете специализировать шаблон:

template <unsigned N> struct A<char[N]> { /* ... */ };
1 голос
/ 08 августа 2010

Вы могли бы просто следовать за ошибкой компиляции и специализироваться на char[5].

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

Это кажется очень странной специализацией, чтобы обеспечить, однако.

Если вы хотите явно указатьa какую специализацию DoSomething использовать, тогда почему бы не сделать именно это?

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

Конечно, вы обнаружите, что это все еще не компилируется, так как DoSomething принимает неконстантную ссылкупоэтому вам нужно lvalue типа char *, временное не подойдет.

int main()
{
  char a[5];
  char *p = a;
  DoSomething<char *>(p);
}
0 голосов
/ 08 августа 2010

Другой возможностью принудительного распада массива-> указателя будет создание специализации 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);
}

(вы должны задать параметр DoSomething const, так как неконстантная ссылка не может работать какссылка, если вы передаете ей массивы.

0 голосов
/ 08 августа 2010
char a[5];
char* aPointer = &a[0];
DoSomething(aPointer);

Это передаст символ * в DoSomething.

Вот ваш полный пример кода, модифицированный для правильной работы:

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);
}
...