std::tolower and Visual Studio 2013

std::tolower and Visual Studio 2013

本文关键字:Studio 2013 Visual and tolower std      更新时间:2023-10-16

我试着了解如何使用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;
}

所以:

  1. 为什么::tolower版本有效?
  2. 为什么std::tolower在 std::transform 中不起作用?
  3. static_cast<int(*)(int)>(std::tolower))到底想做什么?为什么它是否适用于GCC而不是Visual Studio 2013?
  4. 那么,如何在 Visual Studio 2013 中使用 std::transform 中的std::lower呢?

首先,请注意,这些方法都没有以可移植的方式做正确的事情!问题是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的平台上很重要,但这似乎是典型的选择。

要回答您的问题:

  1. 包含<cctype>时,通常从命名空间std和全局命名空间中的标准 C 库中获取各种函数和类型。因此,使用::tolower通常有效,但不能保证有效。
  2. 当包括<locale>时,有两个版本的std::tolower可用,一个作为int(*)(int),一个作为char(*)(char, std::locale const&)。当只使用std::tolower编译器通常无法决定使用哪一个。
  3. 由于std::tolower不明确,因此使用 static_cast<int(*)(int)>(std::tolower) 可以消除使用哪个版本的歧义。为什么将static_cast<...>()与VC++一起使用失败,我不知道。
  4. 无论如何,您都不应该将std::tolower()char 的序列一起使用,因为它会导致未定义的行为。在unsigned char上使用内部使用std::tolower的函数对象。
值得注意的是,使用函数对象而不是函数指针

通常要快得多,因为内联函数对象是微不足道的,但内联函数指针就不那么简单了。编译器在函数指针的实际已知情况下内联使用函数指针变得越来越好,但当代编译器肯定并不总是通过函数指针内联函数调用,即使所有上下文都在那里。

std::tolower

C++ 中重载,则在 <cctype> 中声明为

int tolower(int);

并且在<locale>

template<CharT> CharT tolower(CharT, const locale&);

所以当你说"std::tolower"时,你会得到一个对重载函数的模糊引用。

  1. 为什么::tolower版本有效?

包含<cctype>时,单参数重载将在命名空间std中声明,也可能在全局命名空间中声明,具体取决于编译器。 如果包含<ctype.h>则保证它包含在全局命名空间中,并且::tolower将起作用(尽管请注意 Dietmar 关于何时不安全的观点)。来自 <locale> 的双参数重载永远不会在全局命名空间中声明,因此::tolower从不引用双参数重载。

2. 为什么std::tolower在 std::transform 中不起作用?

见上文,这是一个重载的名称。

3. static_cast<int(*)(int)>(std::tolower))真正想做什么?

它告诉编译器您希望int std::tolower(int)重载,而不是任何其他std::tolower重载。

为什么它适用于GCC而不是Visual Studio 2013?

可能是因为你没有包含<cctype>,或者(不太可能)它可能是Visual Studio的错误。

4. 那么,如何在 Visual Studio 2013 中使用std::lower std::transform

如果您知道只有值介于 0 和 127 之间的字符,则可以包含 <ctype.h> 并使用 ::tolower(因为双参数版本未在全局命名空间中声明,仅在命名空间中声明 std )或消除所需的静态强制转换的歧义。 强制转换的替代方法是使用局部变量:

typedef int (*tolower_type)(int);
tolower_type tl = &std::tolower;
std::transform(b, e, b, tl);

更安全且可移植的替代方法是使用自定义函数对象(或 lambda 表达式)安全地调用所需的重载:

std::transform(b, e, b, [](unsigned char i) { return std::tolower(i); });

这会将std::tolower与参数一起使用,因此编译器可以执行重载解析来告知要调用哪个重载。 unsigned char参数是为了确保我们永远不会将负值的char传递给tolower(int),因为它具有未定义的行为。

有关更多详细信息,请参阅 http://gcc.gnu.org/onlinedocs/libstdc++/manual/strings.html#strings.string.simple。