开发者

How can templated code find the length of a string whose type is a template parameter, in C++?

开发者 https://www.devze.com 2023-01-23 00:29 出处:网络
Consider this code: template <typename T> class String { public: ... String(T* initStr) { size_t initStrLen;

Consider this code:

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

When I compiled the code, I got this error message:

...\main.cpp(32) : error C2664: 'strlen' : cannot convert parameter 1 from 'wchar_t *' to 'const char *'

Then I tried to use a function pointer:

typed开发者_StackOverflow中文版ef size_t (*STRLEN)(void*);
STRLEN _strlen;
_strlen = reinterpret_cast<STRLEN> (typeid(*initStr) == typeid(char) ? strlen : wcslen);

and again the compiler issued an error, this time:

...\main.cpp(28) : error C2446: ':' : no conversion from 'size_t (__cdecl *)(const wchar_t *)' to 'size_t (__cdecl *)(const char *)'

My question is, how can I use the functions strlen and wcslen with templates?


You can do this e.g. by introducing a helper function as illustrated below:

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

Use this helper function GetLength instead of strlen or wcslen and don't check the type of the argument explicitly. You can write overloads of GetLength for other types as well, e.g. std::string.

You rarely need to use typeid in practice and in this case it is completely inappropriate.


You can't use if statements to control what code is instantiated for a template: all of the code in the body must work for every instantiation.

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
  //...

You could also add an overload of your strlen for char, then you don't need the using declaration.


You have misunderstood templates. You should not use typeid to determine types here, but instead use template specialisation.


In case the OP is interested in how strings are implemented in STL, they use a whole helper class call char_traits. This is a class with nothing but static member functions, and char_traits is specialised for char and wchar_t to use the C runtime library functions like memmove.

For example you have a compare function that returns a value <0, 0 or >0. Where the type is char it can use memcmp. Where the type is wchar_t it can use the wide equivalent.

It works something like this:

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
0

精彩评论

暂无评论...
验证码 换一张
取 消