Как шаблонный код может найти длину строки, тип которой является параметром шаблона, в C ++? - PullRequest
3 голосов
/ 31 октября 2010

Рассмотрим этот код:

template <typename T>
class String
{
public:
    ...
 String(T* initStr)
 {
  size_t initStrLen;
  if (initStr != NULL)
  {
   printf_s("%s\n", typeid(T) == typeid(char) ? "char" : "wchar_t");

   if (typeid(T) == typeid(char))
   {
    strlen((T*)initStr);
   }
   else if (typeid(T) == typeid(wchar_t))
   {
    wcslen((T*)initStr);
   }
  }
 }  
    ...
};

Когда я компилировал код, я получил это сообщение об ошибке:

... \ main.cpp (32): ошибка C2664: 'strlen': невозможно преобразовать параметр 1 из 'wchar_t *' в 'const char *'

Затем я попытался использовать указатель на функцию:

typedef size_t (*STRLEN)(void*);
STRLEN _strlen;
_strlen = reinterpret_cast<STRLEN> (typeid(*initStr) == typeid(char) ? strlen : wcslen);

и снова компилятор выдал ошибку, на этот раз:

... \ main.cpp (28): ошибка C2446: ':': нет преобразования из 'size_t (__cdecl *) (const wchar_t *)' в 'size_t (__cdecl *) (const char *)'

У меня вопрос, как я могу использовать функции strlen и wcslen с шаблонами?

Ответы [ 4 ]

7 голосов
/ 31 октября 2010

Вы можете сделать это, например, введя вспомогательную функцию, как показано ниже:

#include <iostream>
#include <string.h>

size_t GetLength(const char* s) { return strlen(s); }
size_t GetLength(const wchar_t* s) { return wcslen(s); }

template <typename T>
void PrintLength(T s)
{
    std::cout << GetLength(s) << std::endl;
}

int main()
{
    PrintLength("abc");
    PrintLength(L"abc");
}

Используйте эту вспомогательную функцию GetLength вместо strlen или wcslen и не проверяйте типаргумент явно.Вы можете записать перегрузки GetLength и для других типов, например std::string.

. На практике вам редко требуется использовать typeid, и в этом случае это совершенно неуместно.

5 голосов
/ 31 октября 2010

Вы не можете использовать операторы if для управления тем, какой код создается для шаблона: весь код в теле должен работать для каждого экземпляра.

std::size_t strlen(wchar_t const *s) {
  return std::wcslen(s);
}

//...
String(T* initStr) {
  using std::strlen;  // bring into scope so unqualified call can find it
  std::size_t length = strlen(initStr);  // uses std::strlen or our strlen
  //...

Вы также можете добавить перегрузку вашего кода.strlen для char, тогда вам не нужно объявление using.

3 голосов
/ 31 октября 2010

Вы неправильно поняли шаблоны.Вы не должны использовать typeid для определения типов здесь, а вместо этого использовать специализацию шаблона.

1 голос
/ 31 октября 2010

В случае, если OP интересуется тем, как строки реализованы в STL, они используют целый класс вспомогательного вызова char_traits. Это класс, в котором нет ничего, кроме статических функций-членов, а char_traits специализируется на использовании char и wchar_t функций библиотеки времени выполнения C, таких как memmove.

Например, у вас есть функция сравнения, которая возвращает значение <0, 0 или> 0. Где тип char, он может использовать memcmp. Если тип wchar_t, он может использовать широкий эквивалент.

Это работает примерно так:

template< typename Element >
class char_traits
{
public:
   static int compare( const Element * left, const Element * right, size_t length )
   {
       for( const Element * end = left + length; left != end; ++left )
       {
          if( left < right )
             return -1;
          else if( left > right )
              return 1;
       }
       return 0;
   }
  // other functions
};

template <> class char_traits<char> // I think this is the syntax
{
public:
   int compare( const char * left, const char * right, size_t len )
   {
     return memcmp( left, right, len ); // more efficient than the general loop above
   }
  // other functions
};

// specialise also for wchar_t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...