为什么不能"转换(s.begin(),s.end(),s.begin(),tolower)"成功编译?

liu*_*liu 28 c++ compiler-errors lowercase toupper tolower

鉴于代码:

#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
     string s("ABCDEFGHIJKL");
     transform(s.begin(),s.end(),s.begin(),tolower);
     cout<<s<<endl;
}
Run Code Online (Sandbox Code Playgroud)

我收到错误:

呼叫没有匹配功能 transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)

什么是"未解决重载函数类型"是什么意思?

如果我用tolower我写的函数替换它,它不再是错误.

dav*_*vka 29

尝试使用::tolower.这解决了我的问题.

  • 这是正确的.tolower和:: tolower有什么区别? (3认同)
  • @liu:就像@David写的那样 - "::"从全局命名空间中选择`tolower` (3认同)
  • 这个答案没有回答这个问题——为什么它不起作用? (2认同)
  • 可以,然后呢。做了一些挖掘。在 std 中,有一个版本的 tolower 需要 `const locale&amp;`,因此 Transform 无法推断出要使用两个重载中的哪一个。由于 tolower 的全局命名空间版本没有重载,因此它工作得很好。话虽如此,msvc 似乎没有这个问题 - *除非*您包含 &lt;locale&gt;。 (2认同)

Dav*_*eas 22

问题很可能与多次重载有关tolower,编译器无法为您选择一个.您可以尝试对其进行限定以选择其特定版本,或者您可能需要提供一个强制歧义的函数指针.该tolower函数可以在<locale>标题中存在(多个不同的重载),也可以在<cctype>.

尝试:

int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );
Run Code Online (Sandbox Code Playgroud)

这可以通过演员阵容在一行中完成,但可能更难以阅读:

transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );
Run Code Online (Sandbox Code Playgroud)

  • 但是不要忘记,如果任何char值为负数,则在上述字符串中使用此版本的tolower是未定义的行为(它们可以在大多数现代系统上,例如,如果存在任何重音字符). (6认同)
  • @liu:请注意,使用`:: tolower`可以在不同的编译器中工作,但它不是标准的.基本上大多数编译器,当你包含`cctype`时,编译器需要提供`int std :: tolower(int)`,但是不需要添加`int :: tolower(int)`,不同的编译器*将*提供两个函数具有相同的实现(其中一个将转发到另一个),但这不是必需的,可能会随着下一个编译器版本而改变(或者如果您更改编译器) (3认同)

Jon*_*Mee 20

让我们看一下从最差开始到最佳状态的选项列表.我们将在此列出它们并在下面讨论它们:

  1. transform(cbegin(s), cend(s), begin(s), ::tolower)
  2. transform(cbegin(s), cend(s), begin(s), static_cast<int(*)(int)>(tolower))
  3. transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })

您问题中的代码transform(s.begin(), s.end(), s.begin(), tolower)会产生如下错误:

呼叫没有匹配功能 transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)

那你得到一个"未解决重载函数型"的原因是有2个tolowerS IN的std命名空间:

  1. locale库定义template <typename T> T tolower(T, const locale&)
  2. cctype库定义int tolower(int)

1davka提供解决方案.它通过利用一个事实,解决你的错误localetolower不全局命名空间中定义.

根据您的情况locale,tolower可能值得考虑.你可以在tolower这里找到s 的比较:C++中的哪个tolower?


不幸的是1取决于cctypetolower全局命名空间被定义.让我们来看看为什么情况可能并非如此:

您正确使用#include <cctype>,因为#include <ctype.h>在C++中已弃用:http://en.cppreference.com/w/cpp/header

但是C++标准在D.3 [depr.c.headers]中声明了头文件中的声明:

未指定是否首先在命名空间的命名空间范围(3.3.6)中声明或定义这些名称std,然后通过显式使用声明将其注入全局命名空间范围(7.3.3)

因此,我们可以保证我们的代码独立于实现的唯一方法是使用tolowerfrom namespace std.2DavidRodríguez提供解决方案 - dribeas.它利用了以下事实static_cast:

用于通过执行到特定类型的函数到指针转换来消除函数重载的歧义

在我们继续之前,让我评论一下,如果您发现int (*)(int)有点混乱,可以在这里阅读更多有关函数指针语法的内容.


可悲的是,输入参数存在另一个问题tolower,如果是:

不能表示为unsigned char且不等于EOF,行为未定义

您使用的string是使用以下类型的元素:char.标准状态char具体为7.1.6.2 [dcl.type.simple] 3:

实现定义了char类型对象是表示为有符号数还是无符号数.该signed说明符的力量char要签名的对象

因此,如果实现将a定义char为意味着a,signed char12都将导致对应于负数的所有字符的未定义行为.(如果使用ASCII字符编码,则与负数对应的字符为扩展ASCII.)

通过将输入转换为unsigned char传递给它之前的输入,可以避免未定义的行为tolower.3实现了使用接受unsigned charby值的lambda ,然后将其传递给tolower隐式转换为int.

为了保证所有兼容实现的定义行为,独立于字符编码,您需要使用transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })或类似的东西.

  • @exilit我相信这是最好的答案.我花时间为一个问题添加一个答案,即使它永远不会被接受,因为我觉得所有其他答案都不尽相同.让某人只是按照投票进行投票是......悲伤.无论如何,谢谢你的确认.很高兴知道有人关心我提高答案质量的努力. (4认同)
  • 我也想知道. (2认同)

Mat*_* M. 6

大卫已经确定了这个问题,即:

  • <cctype>int tolower(int c)
  • <locale>template <typename charT> charT tolower(charT c, locale const& loc)

使用第一个更容易,但是一旦你处理了签名字符中的低-ascii(0-127)之外的任何其他内容,就会出现未定义的行为(不幸).顺便说一下,我建议定义char为unsigned.

模板版本会很好,但你必须使用它bind来提供第二个参数,它必然是丑陋的......

那么,我可以介绍一下Boost String Algorith m库吗?

更重要的是:boost::to_lower:)

boost::to_lower(s);
Run Code Online (Sandbox Code Playgroud)

表现力是可取的.

  • @davka:对.你需要提防第三方库:为了让第三方图书馆能够顺畅地互动,你的程序,所有这些都应该给`char`提供相同的意义.您需要查看他们的文档,以了解他们是否明确了它或检查代码以了解他们是否做出了假设:`while((c = getchar())== EOF)`是一个带有`-funsigned-的无限循环char`,因为`getchar()`返回一个`int`,而不是`char`(和`EOF`是`-1`) (2认同)