【问题标题】:How can templated code find the length of a string whose type is a template parameter, in C++?模板化代码如何在 C++ 中找到类型为模板参数的字符串的长度?
【发布时间】:2011-05-03 00:36:19
【问题描述】:

考虑这段代码:

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 与模板一起使用?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    您可以这样做,例如通过引入如下所示的辅助函数:

    #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,在这种情况下它是完全不合适的。

    【讨论】:

      【解决方案2】:

      您不能使用 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
        //...
      

      您还可以为 char 添加 strlen 的重载,然后就不需要 using 声明。

      【讨论】:

        【解决方案3】:

        您误解了模板。此处不应使用typeid 来确定类型,而应使用模板特化。

        【讨论】:

        • 是的,但最好提供一个编码示例。 vitaut 使用了不同的方法,具有重载的函数。
        • 我的第一个想法也是专业化,但在这里,专业化在实践中意味着编写 2 个版本的类,完全可以在没有模板的情况下完成。
        • 好吧,std::basic_string 使用了一个特化,但不是 basic_string 本身,而是 char_traits。
        【解决方案4】:

        如果 OP 对如何在 STL 中实现字符串感兴趣,他们会使用一个完整的辅助类调用 char_traits。这是一个只有静态成员函数的类,而 char_traits 专门用于 char 和 wchar_t 以使用 memmove 等 C 运行时库函数。

        例如,您有一个返回值 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-03-03
          • 2018-10-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多