std :: transform()和toupper(),没有匹配的函数

我试过这个问题的代码C ++ std :: transform()和toupper()..为什么这会失败?

#include <iostream> #include <algorithm> int main() { std::string s="hello"; std::string out; std::transform(s.begin(), s.end(), std::back_inserter(out), std::toupper); std::cout << "hello in upper case: " << out << std::endl; } 

从理论上讲,它应该是Josuttis书中的例子之一,但它不能编译http://ideone.com/aYnfv 。

为什么GCC抱怨:

 no matching function for call to '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> > >, std::back_insert_iterator<std::basic_string <char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)' 

我在这里错过了什么? 是GCC相关的问题?

只需使用::toupper而不是std::toupper 。 也就是说,在全局命名空间中定义toupper ,而不是在std命名空间中定义的。

 std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper); 

其工作: http : //ideone.com/XURh7

你的代码不工作的原因:在名字空间std有另外一个重载的函数toupper ,当parsing这个名字的时候会造成问题,因为当你简单地通过std::toupper时,编译器无法决定你指的是哪个超载。 这就是为什么编译器在错误信息中说明了unresolved overloaded function type ,这表明存在过载。

所以为了帮助编译器解决正确的重载问题,你必须将std::toupper作为

 (int (*)(int))std::toupper 

也就是说,以下是可行的:

 //see the last argument, how it is casted to appropriate type std::transform(s.begin(), s.end(), std::back_inserter(out),(int (*)(int))std::toupper); 

请自行检查: http : //ideone.com/8A6iV

问题

 std::transform( s.begin(), s.end(), std::back_inserter(out), std::toupper ); 

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> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>) '

这是一个误导性的错误; 有趣的部分并不是说调用没有“匹配的function”,而是为什么没有匹配的function。

为什么你传递一个“ <unresolved overloaded function type> ”的函数引用作为参数,而GCC更喜欢错误的调用,而不是这个重载parsing失败。


说明

首先,你应该考虑如何在C ++中inheritanceC库。 <ctype.h>有一个函数int toupper(int)

C ++inheritance了这个:

[n3290: 21.7/1]:表74,75,76,77,78和79描述了标题<cctype><cwctype><cstring><cwchar><cstdlib> (字符转换), <cuchar> ,分别。

[n3290: 21.7/2]:这些头文件的内容应与标准C库标头<ctype.h><wctype.h><string.h><wchar.h><stdlib.h>和C Unicode TR头文件<uchar.h> ,分别为[..]

[n3290: 17.6.1.2/6]:在C语言中被定义为函数的名字将被定义为C ++标准库中的函数。

但是使用<ctype.h>已经被弃用:

[n3290: C.3.1/1]:为了与标准C库兼容,C ++标准库提供了18 C头文件(D.5),但在C ++中不推荐使用它们。

而访问C toupper是通过C ++向后兼容的头文件<cctype> 。 对于这样的头文件,内容被移动或复制 (取决于你的实现)到std名称空间中:

[n3290: 17.6.1.2/4]: [..]然而,在C ++标准库中,声明(在C中定义为macros的名称除外)位于名称空间std的名称空间范围(3.3.6)内。 没有指定这些名称是否在全局命名空间范围内首先声明,然后通过显式使用声明 (7.3.3) 注入命名空间std

但是C ++库还在头文件<locale>引入了一个新的特定于语言环境的函数模板,这个模板被称为toupper (当然,在命名空间std ):

[n3290: 22.2]: [..] template <class charT> charT toupper(charT c, const locale& loc); [..]

所以,当你使用std::toupper ,有两个重载可供select。 既然你没有告诉GCC你希望使用哪个函数,那么重载就不能被parsing,你对std::transform调用就不能完成了。


差距

现在,这个原始问题的OP没有遇到这个问题。 他可能没有范围内的std::toupper的语言环境版本,但是又没有#include <locale>

然而:

[n3290: 17.6.5.2]: C ++头文件可能包含其他C ++头文件。

所以只要你的<iostream>或者你的<algorithm> ,这些头文件包含的头文件,或者这些头文件包含的头文件等等,都会导致在你的实现中包含<locale>


有两个解决方法。

  1. 您可以提供一个转换子句来强制函数指针指向您希望使用的重载:

     std::transform( s.begin(), s.end(), std::back_inserter(out), (int (*)(int))std::toupper // specific overload requested ); 
  2. 您可以通过明确使用全局toupper来从重载设置中删除区域设置版本:

     std::transform( s.begin(), s.end(), std::back_inserter(out), ::toupper // global scope ); 

    但是,请记住, <cctype>此函数是否可用是未指定的( [17.6.1.2/4] ),并且已使用<ctype.h>[C.3.1/1] )。

    因此,这不是我会推荐的选项。


注意:我鄙视书写尖括号,就好像它们是标题名称的一部分 – 它们是#include语法的一部分,而不是标题名称 – 但是为了与FDIS引用的一致性,我在这里做了它;而且,老实说,更清楚…)