std :: transform () и toupper (), без соответствующей функции - PullRequest
56 голосов
/ 20 августа 2011

Я попробовал код из этого вопроса 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?

Ответы [ 2 ]

68 голосов
/ 20 августа 2011

Просто используйте ::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

44 голосов
/ 20 августа 2011

Задача

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 наследуется в C ++. <ctype.h> имеет функцию int toupper(int).

C ++ наследует это:

[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 заголовок <uchar.h> соответственно [..]

[n3290: 17.6.1.2/6]: Имена, которые определены как функции в C, должны определяться как функции в стандартной библиотеке C ++.

Но использование <ctype.h> устарело:

[n3290: C.3.1/1]: Для совместимости со стандартной библиотекой C Стандартная библиотека C ++ предоставляет заголовки 18 C (D.5), но их использование устарел в C ++.

И способ доступа к C toupper - через заголовок обратной совместимости C ++ <cctype>. Для таких заголовков содержимое либо перемещается, либо копируется (в зависимости от вашей реализации) в пространство имен std:

[n3290: 17.6.1.2/4]: [..] В стандартной библиотеке C ++, однако, объявления (за исключением имен, которые определены как макросы в C) находятся в пределах область имен пространства (3.3.6) пространства имен std. Не указано будут ли эти имена впервые объявлены в глобальном пространстве имен область и затем вводятся в пространство имен стандартным явным декларации использования (7.3.3).

Но библиотека C ++ также вводит новый, специфичный для локали шаблон функции в заголовке <locale>, который также называется toupper (конечно, в пространстве имен std):

[n3290: 22.2]: [..] template <class charT> charT toupper(charT c, const locale& loc); [..]

Итак, когда вы используете std::toupper, вы можете выбрать из двух перегрузок. Поскольку вы не сообщили GCC, какую функцию вы хотите использовать, перегрузка не может быть разрешена, и ваш вызов на std::transform не может быть завершен.


диспаритет

Теперь ОП этого первоначального вопроса не сталкивался с этой проблемой. Скорее всего, у него не было локальной версии std::toupper, но, опять же, у вас тоже не было #include <locale>! 1096 *

Тем не менее:

[n3290: 17.6.5.2]: Заголовок C ++ может включать другие заголовки C ++.

Так что просто так происходит , что либо ваш <iostream> или ваш <algorithm>, либо заголовки, которые включают эти заголовки, либо заголовки, которые эти заголовки включают (и т. Д.), Ведут для включения <locale> в вашу реализацию.


Решение

Есть два обходных пути к этому.

  1. Вы можете предоставить условие преобразования, чтобы заставить указатель функции ссылаться на перегрузку, которую вы хотите использовать:

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       (int (*)(int))std::toupper  // specific overload requested
    );
    
  2. Вы можете удалить версию локали из набора перегрузки, явно используя глобальный 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]).

    Таким образом, я бы не рекомендовал этот вариант.


( Примечание: Я презираю написание угловых скобок, как если бы они были частью имен заголовков & mdash; они являются частью синтаксиса #include, а не имен заголовков & mdash; но я это сделал здесь для согласованности с цитатами FDIS, и, если честно, это является яснее ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...