Подтвердить что ты не робот

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?

4b9b3361

Ответ 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, пожалуйста, включите локаль