Std:: transform() и toupper(), нет соответствующей функции
Я попробовал код из этого вопроса 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;
}
Теоретически он должен был работать как один из примеров в книге Джозуттиса, но он не компилируется http://ideone.com/aYnfv.
Почему GCC жаловался:
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>)
Я что-то здесь упускаю? Это проблема, связанная с GCC?
Ответы
Ответ 1
Просто используйте ::toupper
вместо std::toupper
. То есть toupper
, определяемое в глобальном пространстве имен, вместо имени, определенного в пространстве имен std
.
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
Работает: http://ideone.com/XURh7
Причина, почему ваш код не работает: есть еще одна перегруженная функция toupper
в пространстве имен std
, которая вызывает проблему при разрешении имени, поскольку компилятор не может решить, какую перегрузку вы имеете в виду, когда вы просто пройдите std::toupper
. Вот почему компилятор говорит unresolved overloaded function type
в сообщении об ошибке, что указывает на наличие перегрузки (-ов).
Итак, чтобы помочь компилятору в правильной перегрузке, вы должны использовать std::toupper
как
(int (*)(int))std::toupper
То есть будет работать следующее:
//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);
Проверьте сами: http://ideone.com/8A6iV
Ответ 2
Проблема
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
std::toupper
);
нет соответствующей функции для вызова 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 наследуется на С++. <ctype.h>
имеет функцию int toupper(int)
.
С++ наследует это:
[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 header <uchar.h>
, соответственно [..]
[n3290: 17.6.1.2/6]:
Имена, которые определены как функции в C, должны быть определенными как функции в стандартной библиотеке С++.
Но использование <ctype.h>
устарело:
[n3290: C.3.1/1]:
Для совместимости со стандартной библиотекой C Стандартная библиотека С++ предоставляет заголовки 18 C (D.5), но их использование устарел на С++.
И путь доступа к C toupper
осуществляется через заголовок обратной совместимости С++ <cctype>
. Для таких заголовков содержимое перемещается или копируется (в зависимости от вашей реализации) в пространство имен std
:
[n3290: 17.6.1.2/4]:
[..] Однако в стандартной библиотеке С++ объявления (за исключением имен, которые определены как макросы в C) находятся в пределах пространство имен (3.3.6) пространства имен std. Не указано. объявляются ли эти имена в глобальном пространстве имен и затем вводятся в пространство имен std с помощью явного using-declarations (7.3.3).
Но библиотека С++ также вводит новый, специфичный для локали шаблон функции в заголовке <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]:
Заголовок С++ может содержать другие заголовки С++.
Так получилось, что либо ваш <iostream>
, либо ваш <algorithm>
, или заголовки, включенные в эти заголовки, или заголовки, включенные в эти заголовки (и т.д.), приведут к включению <locale>
в вашу реализацию.
Решение
Для этого есть два способа обхода.
-
Вы можете предоставить предложение преобразования, чтобы принудить указатель функции ссылаться на перегрузку, которую вы хотите использовать:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
(int (*)(int))std::toupper // specific overload requested
);
-
Вы можете удалить версию локали из набора перегрузки, явно используя глобальный toupper
:
std::transform(
s.begin(),
s.end(),
std::back_inserter(out),
::toupper // global scope
);
Однако помните, что независимо от того, доступна ли эта функция в <cctype>
, не указывается ([17.6.1.2/4]
), а использование <ctype.h>
устарело ([C.3.1/1]
).
Таким образом, это не тот вариант, который я бы рекомендовал.
( Примечание: Я презираю угловые скобки, как если бы они были частью имен заголовков — они являются частью синтаксиса #include
, а не имена заголовков — но я сделал это здесь для согласованности с цитатами FDIS, и, честно говоря, это яснее...)
Ответ 3
std::transform(s.begin(), s.end(), s.begin(),
std::bind(&std::toupper<char>, std::placeholders::_1, std::locale()));
Если вы используете vc toolchain, пожалуйста, включите локаль