Почему «transform (s.begin (), s.end (), s.begin (), tolower)» не может быть успешно выполнен? - PullRequest
26 голосов
/ 04 апреля 2011

С учетом кода:

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

Что означает «неразрешенный тип перегруженной функции» ?

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

Ответы [ 5 ]

27 голосов
/ 04 апреля 2011

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

22 голосов
/ 04 апреля 2011

Скорее всего, проблема связана с несколькими перегрузками 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 );
19 голосов
/ 25 мая 2016

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

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

Нет соответствующей функции для вызова transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)

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

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

1 - это решение , предлагаемое davka . Она устраняет вашу ошибку, используя тот факт, что locale tolower не определено в глобальном пространстве имен.

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


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

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

Но стандарт C ++ в D.3 [depr.c.headers] 2 гласит объявления в заголовках:

Не определено, были ли эти имена впервые объявлены или определены в пределах области имен (3.3.6) пространства имен std и затем введены в глобальную область имен с помощью явных объявлений using (7.3.3)

Таким образом, единственный способ гарантировать, что наш код независим от реализации, - это использовать tolower из namespace std. 2 - это решение , предложенное Дэвидом Родригесом - dribeas . Он использует тот факт, что static_cast может:

Используется для устранения неоднозначности перегрузок функций путем выполнения преобразования функции в указатель в конкретный тип

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


К сожалению, есть еще одна проблема с входным аргументом tolower, если она:

не может быть представлен как unsigned char и не равен EOF, поведение не определено

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

Определяется реализацией, представляются ли объекты типа char в виде количеств со знаком или без знака. Спецификатор signed заставляет char объекты быть подписанными

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

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

Чтобы гарантировать определенное поведение во всех совместимых реализациях, независимо от кодировки символов, вам необходимо использовать transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); }) или что-то подобное.

6 голосов
/ 04 апреля 2011

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

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

Использование первого намного проще, но это неопределенное поведение (к сожалению), как только вы столкнетесь с чем-то еще, кроме low-ascii (0-127) в знаковых символах. Кстати, я рекомендую определить char как беззнаковое.

Версия шаблона была бы хороша, но вам нужно было бы использовать bind, чтобы предоставить второй параметр, и он должен быть уродливым ...

Итак, могу я представить библиотеку Boost String m библиотеки?

И что еще более важно: boost::to_lower:)

boost::to_lower(s);

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

4 голосов
/ 04 апреля 2011

Просматривая мой <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.

...