我尝试了解如何使用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;
}
所以:
::tolower
版本有效? std::tolower
无法在std :: transform中运行? static_cast<int(*)(int)>(std::tolower))
真正想要做什么?为什么
它适用于GCC而不适用于Visual Studio 2013吗?std::lower
呢?答案 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
的平台,但这似乎是典型的选择。
回答你的问题:
<cctype>
时,您通常会在命名空间std
以及全局命名空间中从标准C库中获取各种函数和类型。因此,使用::tolower
通常可以正常工作,但不能保证有效。<locale>
时,有std::tolower
个版本可用,一个版本为int(*)(int)
,另一个版本为char(*)(char, std::locale const&)
。当仅使用std::tolower
时,编译器通常无法决定使用哪一个。std::tolower
不明确,因此使用static_cast<int(*)(int)>(std::tolower)
消除了使用哪个版本的歧义。为什么在VC ++中使用static_cast<...>()
失败,我不知道。std::tolower()
与char
s序列一起使用,因为它会导致未定义的行为。在std::tolower
内部使用unsigned char
使用功能对象。值得注意的是,使用函数对象而不是函数指针通常很多更快,因为内联函数对象是微不足道的,但内联函数指针并不简单。编译器在内联函数指针的过程中越来越好,其中函数实际上是已知的,但是当前的编译器肯定并不总是通过函数指针内联函数调用,即使所有上下文都存在。
答案 1 :(得分:7)
std::tolower
在C ++中重载,它在<cctype>
中声明为
int tolower(int);
以及<locale>
中的
template<CharT> CharT tolower(CharT, const locale&);
所以当你说&#34; std::tolower
&#34;你得到一个对重载函数的模糊引用。
- 为什么
醇>::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。我如何在
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。