C++ std::tolower和Visual Studio 2013

C++ std::tolower和Visual Studio 2013,c++,visual-studio,c++11,C++,Visual Studio,C++11,我试图了解如何使用std::tolower #include <iostream> #include <string> #include <algorithm> #include <locale> int main() { std::string test = "Hello World"; std::locale loc; for (auto &c : test) { c = std::to

我试图了解如何使用
std::tolower

#include <iostream>
#include <string>
#include <algorithm>
#include <locale>

int main()
{
    std::string test = "Hello World";
    std::locale loc;
    for (auto &c : test)
    {
        c = std::tolower(c, loc);
    }

    std::transform(test.begin(), test.end(), test.begin(), ::tolower); // 1) OK
    std::transform(test.begin(), test.end(), test.begin(), std::tolower); // 2) Cryptic compile error
    std::transform(test.begin(), test.end(), test.begin(), static_cast<int(*)(int)>(std::tolower)); // 3) Cryptic compile error. Seems OK with other compilers though

    return 0;
}
#包括
#包括
#包括
#包括
int main()
{
std::string test=“Hello World”;
std::locale loc;
用于(自动和控制:测试)
{
c=标准::tolower(c,loc);
}
std::transform(test.begin(),test.end(),test.begin(),::tolower);//1)OK
std::transform(test.begin(),test.end(),test.begin(),std::tolower);//2)神秘的编译错误
std::transform(test.begin()、test.end()、test.begin()、static_cast(std::tolower));//3)神秘的编译错误。不过其他编译器似乎没有问题
返回0;
}
因此:

  • 为什么
    ::tolower
    版本正在运行
  • 为什么
    std::tolower
    在std::transform中不起作用
  • 静态广播(std::tolower))真正想做什么?为什么? 它是否适用于GCC而不适用于Visual Studio 2013
  • 那么,如何在Visual Studio 2013的std::transform中使用
    std::lower

  • <>代码> STD::ToWoW在C++中被重载,在<代码> <代码>中声明为

    int tolower(int);
    
    template<CharT> CharT tolower(CharT, const locale&);
    
    以及在
    中作为

    int tolower(int);
    
    template<CharT> CharT tolower(CharT, const locale&);
    
    更安全和可移植的替代方法是使用自定义函数对象(或lambda表达式)安全地调用所需的重载:

    std::transform(b, e, b, [](unsigned char i) { return std::tolower(i); });
    
    这将使用带有参数的
    std::tolower
    ,因此编译器可以执行重载解析来告诉您要调用哪个重载。参数为
    unsigned char
    ,以确保我们不会将带有负值的
    char
    传递给
    tolower(int)
    ,因为这具有未定义的行为


    请参阅以了解更多详细信息。

    首先,请注意,这些方法中没有一种能以可移植的方式做正确的事情!问题是,
    char
    可能是有符号的(通常是有符号的),但是
    tolower()
    的版本只接受正值!这就是您真正想要使用的
    std::tolower()
    使用如下内容:

    std::transform(test.begin(), test.end(), test.begin(),
                   [](unsigned char c) { return std::tolower(c); });
    
    (当然,如果您一直使用C++03,也可以使用相应的函数对象)。使用带有负值的
    std::tolower()
    (或
    ::tolower()
    )会导致未定义的行为。当然,这只在
    char
    被签名的平台上才重要,然而,这似乎是典型的选择

    回答您的问题:

  • 当包含
    时,通常会从命名空间
    std
    以及全局命名空间中的标准C库中获得各种函数和类型。因此,使用
    ::tolower
    正常工作,但不能保证工作正常
  • 当包含
    时,有两个版本的
    std::tolower
    可用,一个为
    int(*)(int)
    ,另一个为
    char(*)(char,std::locale const&)
    。当只使用std::tolower时,编译器通常无法决定使用哪一个
  • 由于
    std::tolower
    不明确,因此使用
    static_cast(std::tolower)
    可以消除使用哪个版本的歧义。我不知道为什么在VC++中使用
    static\u cast()
    失败了
  • 无论如何,您不应该将
    std::tolower()
    char
    s序列一起使用,因为这将导致未定义的行为。在
    无符号字符上内部使用
    std::tolower
    使用函数对象

  • 值得注意的是,使用函数对象而不是函数指针通常要快得多,因为内联函数对象很简单,而内联函数指针则不那么简单。编译器在函数实际已知的地方内联函数指针的使用越来越好,但当代编译器肯定不会总是通过函数指针内联函数调用,即使所有上下文都在那里。

    您是否尝试过包含
    头、输出、Oo'^^^但是,为什么它与GCC一起工作?@Korchkidu,因为在GCC中,您包含的另一个头恰好包含
    。千万不要误以为“它恰好与一个编译器一起工作”,因为这意味着代码实际上是正确的,尤其是关于包含其他库内部头的头。@JonathanWakely:谢谢,确实很清楚。你应该知道,不要建议人们将
    函数与
    char
    s一起使用!我不知道这是否也适用于VS2013,但VS2012的文档指出,除非isupper返回非零,否则在ctype.h中声明的tolower是不安全的,这意味着在转换中使用它需要对序列中的每个元素在isupper上进行显式测试。我知道我确实看到了大量忽略此约束的代码,但我很好奇这里是否有人对此有任何见解。@Neutrino:C标准(ISO/IEC 9899:2011;早期版本与我所知相同)在7.4.2.1第3段(返回)中对此主题的表述如下:“如果参数是
    isupper
    为真的字符,并且有一个或多个由当前区域设置指定的对应字符,
    islower
    为真,则
    tolower
    函数返回一个对应字符(对于任何给定区域设置,始终相同);否则,参数将原封不动地返回。“除了作为一个有效的
    无符号字符
    值之外,我看不到对参数的任何约束。Visual Studio是否遵守该标准,或者如其文档所示,是否添加了额外的非标准一致约束。这里非常清楚地指出,如果isupper没有为参数返回true,那么结果可能是“意外的”,不管这意味着什么@中微子:我不知道MSVC++是否有符合标准的
    tolower
    实现。不过,我想他们会的。