std :: tolower和Visual Studio 2013

时间:2013-11-09 13:52:05

标签: 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::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呢?

2 个答案:

答案 0 :(得分:8)

首先请注意,这些方法的 none 以便携方式做正确的事情!问题是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)消除了使用哪个版本的歧义。为什么在VC ++中使用static_cast<...>()失败,我不知道。
  4. 您不应该将std::tolower()char s序列一起使用,因为它会导致未定义的行为。在std::tolower内部使用unsigned char使用功能对象。
  5. 值得注意的是,使用函数对象而不是函数指针通常很多更快,因为内联函数对象是微不足道的,但内联函数指针并不简单。编译器在内联函数指针的过程中越来越好,其中函数实际上是已知的,但是当前的编译器肯定并不总是通过函数指针内联函数调用,即使所有上下文都存在。

答案 1 :(得分:7)

std::tolower在C ++中重载,它在<cctype>中声明为

int tolower(int);

以及<locale>中的

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

所以当你说&#34; std::tolower&#34;你得到一个对重载函数的模糊引用。

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

如果包含<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。我如何在std::lower中使用std::transform和Visual Studio 2013呢?

如果您知道只有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