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

Sta*_*eem 57 c++ algorithm stl

我尝试了这个问题的代码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;
}
Run Code Online (Sandbox Code Playgroud)

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

为什么海湾合作委员会抱怨:

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>)’
Run Code Online (Sandbox Code Playgroud)

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

Naw*_*waz 70

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

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
Run Code Online (Sandbox Code Playgroud)

它的工作原理:http://ideone.com/XURh7

您的代码无效的原因:toupper命名空间中有另一个重载函数,std在解析名称时会导致问题,因为当您只是传递时,编译器无法确定您所指的是哪个重载std::toupper.这就是编译器unresolved overloaded function type在错误消息中说的原因,它表明存在重载.

因此,为了帮助编译器解决正确的重载,你需要转换std::toupper

(int (*)(int))std::toupper
Run Code Online (Sandbox Code Playgroud)

也就是说,以下方法可行:

//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);
Run Code Online (Sandbox Code Playgroud)

自行查看:http://ideone.com/8A6iV

  • 如果你使用`:: toupper`,最好包含C头(`<ctype.h>`)以确保它在全局命名空间中.然后,您将依赖于已弃用但指定良好的行为,而不是未指定的行为. (4认同)
  • @Tomalak:这就是我所说的 *"..overloaded function toupper in the namespace std"* 这意味着 `std::toupper` 有重载。 (2认同)
  • @Oli:`&lt;locale&gt;`定义`template &lt;class charT&gt; charT toupper(charT c,const locale&loc)` (2认同)
  • 对不起,但这个答案的开头("只使用toupper而不是std :: toupper")是不完整的.如果OP使用`using namespace std`然后[它不再是一个可行的解决方法](http://ideone.com/GiJw2)(更不用说你使用已弃用或未指定的行为,即使它_does_工作) . (2认同)

Lig*_*ica 45

问题

std::transform(
    s.begin(),
    s.end(),
    std::back_inserter(out),
    std::toupper
);
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> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)'

这是一个误导性的错误; 有趣的部分并不是呼叫的"没有匹配功能",而是为什么没有匹配的功能.

原因是,你传递的"函数引用<unresolved overloaded function type>"作为参数,并GCC喜欢在电话会议上,而不是在这个重载解析失败错误.


说明

首先,您应该考虑如何在C++中继承C库.<ctype.h>有功能int toupper(int).

C++继承了这个:

[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中定义为宏的名称除外)都在命名空间std的命名空间范围(3.3.6)内.未指定是否首先在全局命名空间范围内声明这些名称,然后通过显式using-declarations将其注入命名空间std(7.3.3).

但C++库还介绍了在头一个新的区域设置特定的函数模板<locale>,这被称为toupper(当然,在命名空间std):

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

因此,当您使用时std::toupper,有两个重载可供选择.由于您没有告诉GCC您希望使用哪种功能,因此无法解决过载问题,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
    );
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您可以通过显式使用global来从重载集中删除语言环境版本toupper:

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       ::toupper                  // global scope
    );
    
    Run Code Online (Sandbox Code Playgroud)

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

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


(注:我鄙视写尖括号,如果他们头名的一部分-他们的部分#include语法,而不是标题名称-但我引用了在这里做它用FDIS一致性;而且,说实话,这更清晰...)

  • **注意:**这有点傻,因为`std :: transform`本身正在寻找一个特定的函数签名,所以理论上编译器可以为你推导出解决方案1.但是,它很复杂; 并欢迎使用C++. (4认同)
  • ...这似乎是问题所在,或者至少是其中的重要部分。为了确定这是否是行为差异的原因,我编写了一个简单的程序,该程序读取一个字符串,并使用`toupper()`对其进行转换,然后输出结果。它包括`&lt;iostream&gt;`,`&lt;string&gt;`,&lt;cctype&gt;,&lt;locale&gt;和&&lt;algorithm&gt;。似乎很有趣的是,MSVC 2010能够正确推断出您想要哪个版本的`toupper()`,而更高版本(或至少2015年版本)则不需要。只要您不显式包含`&lt;locale&gt;`,MSVC 2015就可以了,但是如果您这样做,则无法解决重载。 (2认同)