C++ transform算法中toupper的使用

C++ transform算法中toupper的使用

前言

用到这样一个函数,将小写字符串转为大写。

// 一开始个人是这样写的,这样没问题
std::transform(res.begin(), res.end(), res.begin(),[](unsigned char c) { return std::toupper(c); });
// 然后,看到这样的写法
std::transform(res.begin(), res.end(), res.begin(),::toupper);
// 哎,::toupper前面怎么没加std,但加上后就会报错,正文为此问题的探究
// No matching function for call to 'transform' candidate template ignored: couldn't infer template argument '_UnaryOperation' candidate function template not viable: requires 5 arguments, but 4 were provided

正文

在C++中,std::toupper 函数有两种版本,分别定义在头文件 中:

  1. 全局函数版本:

    int toupper(int c);
    // 同所有其他来自  的函数,若参数值既不能表示为 unsigned char 又不等于 EOF 则 std::toupper 的行为未定义。为了以简单的 char (或 signed char )安全使用此函数,首先要将参数转换为 unsigned char,这是因为当字符的值是负数时,传参给int会发生符号扩展,使用 unsigned char 类型可以确保在字符转换时不会发生符号扩展,从而得到正确的结果。
    char my_toupper(char ch){
        return static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
    }
    // 类似
    std::string str_toupper(std::string s) {
        std::transform(s.begin(), s.end(), s.begin(), 
                    // static_cast(std::toupper)         // 错误
                    // [](int c){ return std::toupper(c); }           // 错误
                    // [](char c){ return std::toupper(c); }          // 错误
                       [](unsigned char c){ return std::toupper(c); } // 正确
                      );
        return s;
    }
    

    这是最常用的版本,来自标准 C 库函数,且同时位于全局和 std 名字空间,接受一个整数参数 c,返回将小写字母转换为大写字母后的整数值。如果 c 不是小写字母,它将保持不变。

  2. 函数模板版本:

    template< class CharT >
    CharT toupper( CharT ch, const std::locale& loc );
    

    这个版本是函数模板,接受一个字符 ch 和一个 std::locale 对象。它返回将字符 ch 在指定语言环境下转换为大写形式的结果。这个版本通常用于多语言环境下的字符转换。

在这两个版本中,全局函数版本通常用得更广泛,而函数模板版本用于特定的多语言环境需求。在实际编程中,通常使用全局函数版本来进行字符大小写转换。

故当transform 函数(也是一个模板函数)的第四个参数是std::toupper的时候,此时给定的 tolower 只是作为一个函数指针使用,缺乏类型推导所需要的函数参数信息,所以无法推导出函数的类型,也就无法决定使用哪一个重载函数。故解决方法如下:

  • 指明函数类型

    std::transform(res.begin(), res.end(), res.begin(),(int(*)(int))std::toupper);
    
  • 使用包装函数确定函数类型

    int my_toupper( int c ) {
    return toupper( c ); // 根据 c 的类型可以确定使用 toupper 的哪个重载函数。
    }
    // my_toupper 是非模版非重载函数,避免了函数重载带来的类型解析问题。
    transform( s.begin(), s.end(), s.begin(), my_toupper );
    
  • 调用全局下的tolower函数,版本1和2都位于std命名空间下,但版本1还位于全局空间下,故可以直接指定命名空间为全局的那个tolower函数

    std::transform(res.begin(), res.end(), res.begin(),::toupper);
    

故tolower函数也是一样的。

你可能感兴趣的:(C++,c++,算法,开发语言)