2011-04-04 8 views
23

Учитывая код:Почему не удается «преобразовать (s.begin(), s.end(), s.begin(), 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; 
} 

Я получаю ошибку:

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

Что "нерешенным перегруженная функция типа" означает?

Если я заменил tolower на функцию, которую я написал, она больше не является ошибкой.

+3

Возвращаемый тип 'main' является' int', а возвращаемые типы в C++ должны быть явными. Некоторые компиляторы позволят код как отправленный, но он нестандартен, и он может сломаться с новой версией компилятора или другими компиляторами. –

+1

@ DavidRodríguez-dribeas Возврат из 'main' не требуется C или C++, он неявно возвращает 0. См. Комментарии к этому ответу: http://stackoverflow.com/a/33442842/2642059 –

ответ

27

попробуйте использовать ::tolower. Эта проблема решена, для меня

+3

Это правильно. В чем разница между tolower и :: tolower? – liu

+2

@liu: это как @David написал - «::» выбирает 'tolower' из глобального пространства имен – davka

22

Проблема, скорее всего, связана с несколькими перегрузками tolower, и компилятор не может выбрать один для вас. Вы можете попробовать отдать его, чтобы выбрать конкретную версию, или вам может потребоваться предоставить указатель функции, чтобы устранить неоднозначность. Функция tolower может присутствовать (несколько различных перегрузок) в заголовке <locale>, а также в <cctype>.

Try:

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); 
+5

Но не забывайте что использование этой версии tolower в строке, подобной приведенной выше, является неопределенным поведением, если какое-либо из значений char отрицательно (что они могут быть в большинстве современных систем, например, если присутствуют какие-либо акцентированные символы). –

+1

@James Kanze: Хороший момент, я решил, что перегрузка от чтения исходного сообщения (где cctype явно включен, а локаль не является). Кроме того, функции в локали принимают более одного аргумента, а это значит, что код добавит несвязанную сложность с помощью 'bind' или' bind2nd', чтобы предоставить стандартную 'locale' ... –

+0

Спасибо. Я понимаю проблему , И использование :: tolower может решить проблему – liu

4

Просмотр мой <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 

Так выглядит tolower существует как в std (от <cctype>), так и от корня (от <ctype.h>). Я не уверен, что делает #pragma.

+1

прагматические сигналы gcc, что этот файл является системным заголовком. Это, как правило, влияет на диагноз, так как считается, что компилятор считается неправильным для того, чтобы выдавать предупреждения для заголовков, с которыми он был связан и не должен быть изменен. –

6

Дэвид уже определил проблему, а именно конфликт между:

  • <cctype> 's int tolower(int c)
  • <locale>' s 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); 

выразительность желательно.

+0

1) Не могли бы вы объяснить, что вы подразумеваете под «Я рекомендую определить char как unsigned»? 2) делает 'boost :: to_lower' принимает некоторый набор символов, например. латино-1? – davka

+0

@ davka: 1) стандарт C++ не уточняет, подписан ли символ или нет. Вы можете квалифицировать его, если хотите быть уверенным. Однако ряд функций (например, 'int tolower (int)') имеют неопределенное поведение при вызове с отрицательным символом 'char' ... Посмотрите на свой компилятор, может быть, переключатель или нормальный по умолчанию. 2) 'boost :: to_lower' основан на функции Clow' tolower' и, таким образом, зависит от фасета 'std :: locale' и' ctype', на который он был пропитан. Обратите внимание, что эти грани не могут обрабатывать кодировку с несколькими символами в любом случае ... –

+0

спасибо, я до сих пор не получаю # 1. Я знаю, что 'char' зависит от реализации. Вы предлагаете 'typedef unsigned char char'? это законно? – davka

7

Давайте рассмотрим список вариантов, начиная с наихудшего и перейдя к лучшему. Мы их здесь и обсуждать их ниже:

  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) выдаст ошибку типа:

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

Причина, по которой вы получали в «неразрешенный перегруженную функцию типа» есть 2 tolower s в std пространстве имен:

  1. locale библиотека определяет template <typename T> T tolower(T, const locale&)
  2. cctype библиотека определяет int tolower(int)

- solution offered by davka. Он устраняет вашу ошибку, используя тот факт, что localetolower не определен в глобальном пространстве имен.

В зависимости от вашей ситуации locale's tolower может заслуживать рассмотрения. Вы можете найти сравнение tolower с здесь: Which tolower in C++?


К сожалению зависит от cctype «s tolower определяется в глобальном пространстве имен. Давайте посмотрим, почему это не может быть так:

Вы правильно используете #include <cctype>, как делать #include <ctype.h> устаревшим в C++: http://en.cppreference.com/w/cpp/header

Но 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 std and are then injected into the global namespace scope by explicit using-declarations (7.3.3)

Таким образом, единственный способ, которым мы можем гарантировать наш код не зависит от реализации является использование tolower от namespace std. - solution offered by David Rodríguez - dribeas.Он использует тот факт, что static_cast может:

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

Прежде чем двигаться дальше, позвольте мне прокомментировать, что если вы нашли int (*)(int) быть немного запутанным вы можете прочитать больше о функции указателя синтаксиса here.


К сожалению, есть one other issue с входным аргументом tolower «s, если оно:

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

Вы используете string, который использует элементы типа: char. Стандартные состояния char специфически 7.1.6.2 [dcl.type.simple] 3:

It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects to be signed

Так, если реализация определили char, чтобы означать signed char то оба и приведет к непредсказуемому поведению для всех символов, соответствующих отрицательным числам. (Если кодировка ASCII-символов используется символы, соответствующие отрицательные числа Extended ASCII.)

Неопределенное поведение можно избежать путем преобразования вход на unsigned char перед передачей его в tolower. выполняет то, что использует лямбда, которая принимает значение unsigned char по значению, затем передает его на tolower, неявно преобразуя его в int.

Чтобы гарантировать Определенное поведение на всех совместимых реализациях, независимо от кодировки символов, вам нужно будет использовать или что-то подобное.

+0

@ Downvoter Любое обоснование за нижним углом? –

+2

Хотелось бы это узнать. – exilit

+0

@exilit Я считаю, что это лучший ответ здесь. Я потратил время, чтобы добавить ответ на вопрос, даже если он никогда не будет принят, потому что я чувствовал, что все другие ответы так или иначе не соответствовали. Чтобы кто-то просто проехал по голосу ... грустно. В любом случае спасибо за подтверждение. Приятно знать, что кто-то заботится о моих усилиях по улучшению качества ответа. –