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

时间:2011-04-04 13:33:11

标签: 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;
}

我收到错误:

  

调用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>)

时没有匹配功能

&#34;未解决的重载功能类型&#34; 是什么意思?

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

5 个答案:

答案 0 :(得分:27)

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

答案 1 :(得分:22)

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

尝试:

int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );

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

transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );

答案 2 :(得分:18)

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

  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); })
  4. 您问题中的代码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>)

    时没有匹配功能

    您获得“未解析的重载函数类型”的原因是tolower命名空间中有2个std

    1. locale库定义template <typename T> T tolower(T, const locale&)
    2. cctype库定义int tolower(int)
    3. 1 solution offered by davka。它通过利用全局命名空间中未定义locale的{​​{1}}这一事实来解决您的错误。

      根据您的情况,tolower locale可能值得考虑。您可以在此处找到tolower的比较:Which tolower in C++?

      不幸的是, 1 取决于tolower在全局命名空间中定义的cctype。让我们来看看为什么情况可能并非如此:

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

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

        

      未指定是在名称空间#include <ctype.h>的名称空间作用域(3.3.6)中首先声明或定义这些名称,然后通过显式使用声明(7.3.3)将其注入全局名称空间作用域< / p>

      因此,我们可以保证代码与实现无关的唯一方法是使用std中的tolower 2 solution offered by David Rodríguez - dribeas。它利用static_cast可以:

      的事实
        

      用于通过执行到特定类型

      的函数到指针转换来消除函数重载的歧义

      在我们继续之前,让我评论一下,如果您发现namespace std有点混乱,您可以阅读有关函数指针语法here的更多信息。

      可悲的是one other issueint (*)(int)的输入参数,如果是:

        

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

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

        

      实现定义了char类型的对象是表示为有符号数还是无符号数。 char说明符强制signed个对象被签名

      因此,如果实现将char定义为char,那么 1 2 都会导致所有字符对应的未定义行为到负数。 (如果使用ASCII字符编码,则与负数对应的字符为Extended ASCII。)

      通过将输入转换为signed char,然后将其传递给unsigned char,可以避免未定义的行为。 3 使用接受tolower值的lambda,然后将其传递给隐式转换为unsigned char的{​​{1}}。

      要保证所有合规实施的定义行为,与字符编码无关,您需要使用tolower或类似内容。

答案 3 :(得分:6)

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

之间的冲突
  • <cctype>int tolower(int c)
  • <locale>template <typename charT> charT tolower(charT c, locale const& loc)

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

模板版本会很好,但你必须使用bind来提供第二个参数,而且它一定很难看......

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

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

boost::to_lower(s);

表达力是可取的。

答案 4 :(得分:4)

从gcc 4.2.1浏览我的<ctype>标题,我看到了:

// -*- C++ -*- forwarding header.

// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
// Free Software Foundation, Inc.

...

#ifndef _GLIBCXX_CCTYPE
#define _GLIBCXX_CCTYPE 1

#pragma GCC system_header

#include <bits/c++config.h>
#include <ctype.h>

// Get rid of those macros defined in <ctype.h> in lieu of real functions.
#undef isalnum
#undef isalpha

...

#undef tolower
#undef toupper

_GLIBCXX_BEGIN_NAMESPACE(std)

  using ::isalnum;
  using ::isalpha;

...

  using ::tolower;
  using ::toupper;

_GLIBCXX_END_NAMESPACE

#endif

因此,tolower(来自std)和root(来自<cctype>)名称空间中都存在<ctype.h>。我不确定#pragma做了什么。