【问题标题】:std::tolower and Visual Studio 2013std::tolow 和 Visual Studio 2013
【发布时间】:2013-11-09 13:52:05
【问题描述】:

我试着了解如何使用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&lt;int(*)(int)&gt;(std::tolower)) 真正想要做什么?为什么 它是否适用于 GCC 而不适用于 Visual Studio 2013?
  4. 那我如何在 std::transform 中使用 std::lower 和 Visual Studio 2013?

【问题讨论】:

  • 您是否尝试过包含&lt;cctype&gt; 标头which is where std::tolower is actually defined
  • oups, Oo' ^^' 但是,为什么它与 GCC 一起工作?
  • @Korchkidu,因为使用 GCC,您包含的其他标头之一恰好包含 &lt;cctype&gt;。永远不要将“它恰好与一个编译器一起工作”误认为意味着代码实际上是正确的,尤其是对于内部包含其他库头文件的头文件。
  • @JonathanWakely:谢谢,确实很清楚。

标签: c++ visual-studio c++11


【解决方案1】:

首先,请注意,这些方法中没有一个以可移植的方式做正确的事情!问题是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. 当包含&lt;cctype&gt; 时,您通常会在命名空间std 以及全局命名空间中从标准C 库中获取各种函数和类型。因此,使用::tolower 正常工作,但不能保证工作。
  2. 当包含&lt;locale&gt; 时,有两个版本的std::tolower 可用,一个是int(*)(int),一个是char(*)(char, std::locale const&amp;)。仅使用 std::tolower 时,编译器通常无法决定使用哪一个。
  3. 由于std::tolower 不明确,使用static_cast&lt;int(*)(int)&gt;(std::tolower) 可以消除使用哪个版本的歧义。为什么在 VC++ 中使用 static_cast&lt;...&gt;() 会失败,我不知道。
  4. 无论如何,您都不应该将std::tolower()chars 序列一起使用,因为它会导致未定义的行为。在 unsigned char 内部使用使用 std::tolower 的函数对象。

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

【讨论】:

  • 我不知道 VS2013 是否也是如此,但 VS2012 的文档指出,在 ctype.h 中声明的 tolower 使用不安全,除非 isupper 返回非零值,这在表面上意味着在转换中使用它需要对序列中的每个元素在 isupper 上进行显式测试。我知道我当然已经看到了大量忽略此约束的代码,所以我很想知道这里是否有人对此有任何见解。
  • @Neutrino:这就是 C 标准(ISO/IEC 9899:2011;据我所知,早期版本所说的相同)在 7.4.2.1 第 3 段( Returns): "如果参数是一个字符,isupper 为真,并且有一个或多个对应的字符,由当前语言环境指定,islower 为真,则tolower 函数返回一个对应的字符(对于任何给定的语言环境总是相同的);否则,参数将原样返回。”除了作为有效的unsigned char 值之外,我没有看到对参数的任何限制。
  • Visual Studio 是否遵守标准,或者,正如其文档所建议的那样,它是否添加了额外的非标准一致性约束。它在这里非常清楚地表明,如果 isupper 没有为参数返回 true,那么无论这意味着什么,结果可能是“意外的”。 msdn.microsoft.com/en-us/library/8h19t214.aspx
  • @Neutrino:MSVC++ 是否有符合标准的tolower 实现我不知道。但是,我认为他们会这样做。
【解决方案2】:

std::tolower 在 C++ 中被重载,它在 &lt;cctype&gt; 中声明为

int tolower(int);

还有&lt;locale&gt;

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

所以当你说“std::tolower”时,你会得到一个对重载函数的模棱两可的引用。

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

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

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

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

3。 static_cast&lt;int(*)(int)&gt;(std::tolower)) 到底想做什么?

它告诉编译器你想要int std::tolower(int) 重载,而不是std::tolower 的任何其他重载。

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

可能是因为您没有包含 &lt;cctype&gt;,或者(不太可能)它可能是 Visual Studio 错误。

4。那么我如何在 Visual Studio 2013 中使用 std::transform 中的 std::lower 呢?

如果您知道您只有值介于 0 和 127 之间的字符,那么您可以包含 &lt;ctype.h&gt; 并使用 ::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

【讨论】:

  • 你应该知道最好不要建议人们将&lt;cctype&gt; 函数与chars 一起使用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-08
  • 1970-01-01
相关资源
最近更新 更多