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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5539249/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:23:41  来源:igfitidea点击:

Why can't "transform(s.begin(),s.end(),s.begin(),tolower)" be complied successfully?

c++compiler-errorslowercasetouppertolower

提问by liu

Given the code:

鉴于代码:

#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;
}

I get the error:

我收到错误:

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

没有匹配的函数调用 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>)

What does "unresolved overloaded function type"mean?

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

If I replace the tolowerwith a function I wrote, it no longer errors.

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

采纳答案by davka

Try using ::tolower. This fixed the problem for me.

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

回答by David Rodríguez - dribeas

The problem most probably relates with multiple overloads of tolowerand the compiler is unable to select one for you. You can try qualifying it to select an specific version of it, or you might need to provide a function pointer cast to disambiguate. The tolowerfunction can be present (multiple different overloads) in the <locale>header, as well as in <cctype>.

该问题很可能与 的多个重载有关tolower,并且编译器无法为您选择一个。您可以尝试限定它以选择它的特定版本,或者您可能需要提供一个函数指针来消除歧义。该tolower函数可以出现在<locale>头文件中(多个不同的重载),也可以出现在<cctype>.

Try:

尝试:

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

That can be done in a single line with a cast, but it is probably harder to read:

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

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

回答by Jonathan Mee

Let's look at a list of options starting with the worst and moving to the best. We'll list them here and discuss them below:

让我们看一下从最坏开始到最好的选项列表。我们将在此处列出它们并在下面讨论它们:

  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); })
  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); })


The code in your question, transform(s.begin(), s.end(), s.begin(), tolower)will produce an error like:

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

No matching function for call to transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)

没有匹配的函数调用 transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)

The reason that you were getting an "unresolved overloaded function type" is there are 2 tolowers in the stdnamespace:

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

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

1is the solution offered by davka. It addresses your error by leveraging the fact that locale's toloweris not defined in the global namespace.

1davka 提供解决方案。它通过利用locale'stolower未在全局命名空间中定义的事实来解决您的错误。

Depending upon your situation locale's tolowermay merit consideration. You can find a comparison of the tolowers here: Which tolower in C++?

根据您的情况localetolower可能值得考虑。您可以在tolower此处找到s的比较:C++ 中的哪个 tolower?



Unfortunately 1depends upon cctype's tolowerbeing defined in the global namespace. Let's look at why that may not be the case:

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

You are rightly using #include <cctype>, as doing #include <ctype.h>has been deprecated in C++: http://en.cppreference.com/w/cpp/header

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

But the C++ standard states in D.3[depr.c.headers]2 of the declarations in the headers:

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

It is unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace stdand are then injected into the global namespace scope by explicit using-declarations (7.3.3)

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

So the only way that we can guarantee our code is implementation independent is to use a tolowerfrom namespace std. 2is the solution offered by David Rodríguez - dribeas. It leverages the fact that static_castcan:

因此,我们可以保证我们的代码与实现无关的唯一方法是使用tolowerfrom namespace std2David Rodríguez-dribeas 提供解决方案。它利用了以下事实static_cast

Be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type

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

Before we move on, let me comment that if you find int (*)(int)to be a bit confusing you can read more on function pointer syntax here.

在我们继续之前,让我评论一下,如果你觉得int (*)(int)有点困惑,你可以在这里阅读更多关于函数指针语法的信息



Sadly there is one other issuewith tolower's input argument, if it:

可悲的是,的输入参数还有另一个问题tolower,如果它:

Is not representable as unsigned char and does not equal EOF, the behavior is undefined

不可表示为无符号字符且不等于 EOF,行为未定义

You are using a stringwhich uses elements of type: char. The standard states of charspecifically 7.1.6.2[dcl.type.simple]3:

您正在使用 a string,它使用类型为: 的元素charchar具体7.1.6.2[dcl.type.simple]3的标准状态:

It is implementation-defined whether objects of chartype are represented as signed or unsigned quantities. The signedspecifier forces charobjects to be signed

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

So if the implementation defined a charto mean a signed charthen both 1and 2would result in Undefined Behavior for all characters corresponding to negative numbers. (If an ASCII character encoding is being used the characters corresponding to negative numbers are Extended ASCII.)

因此,如果实现定义了 achar表示 a,signed char那么12都会导致与负数对应的所有字符的未定义行为。(如果使用 ASCII 字符编码,则对应于负数的字符是Extended ASCII。)

The Undefined Behavior can be avoided by converting the input to an unsigned charbefore passing it to tolower. 3accomplishes that using a lambda that accepts an unsigned charby value, then passes it to tolowerimplicitly converting to int.

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

To guarantee Defined Behavior on all compliant implementations, independent of character encoding, you'll need to use transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })or something similar.

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

回答by Matthieu M.

David already identified the issue, namely a conflict between:

David 已经确定了这个问题,即以下之间的冲突:

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

Using the first is much easier, but is undefined behavior (unfortunately) as soon as you deal with anything else than lower-ascii (0-127) in signed chars. By the way, I do recommend defining charas unsigned.

使用第一个要容易得多,但是一旦处理除符号字符中的低位 ascii (0-127) 以外的任何其他内容,就会出现未定义的行为(不幸的是)。顺便说一下,我建议定义char为无符号。

The template version would be nice, but you would have to use bindto provide the second parameter, and it's bound to be ugly...

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

So, may I introduce the Boost String Algorithm library ?

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

And more importantly: boost::to_lower:)

更重要的是boost::to_lower:)

boost::to_lower(s);

Expressiveness is desirable.

表现力是可取的。

回答by Mike DeSimone

Browsing my <ctype>header from gcc 4.2.1, I see this:

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

// -*- 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

So it looks like tolowerexists in both the std(from <cctype>) and root (from <ctype.h>) namespaces. I'm not sure what the #pragmadoes.

所以它看起来tolower同时存在于std(from <cctype>) 和 root (from <ctype.h>) 命名空间中。我不确定它有什么#pragma作用。