std::transform() et toupper(), aucune fonction correspondante

j'ai essayé le code de cette question c++ std::transform() et toupper() ..pourquoi cet échec?

#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;
}

théoriquement, ça aurait dû marcher car C'est l'un des exemples du livre de Josuttis, mais il ne compile pas http://ideone.com/aYnfv .

pourquoi GCC s'est-il plaint:

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

est-ce que je manque quelque chose ici? Est-ce un problème lié au GCC?

53
demandé sur Community 2011-08-20 16:53:55

2 réponses

il suffit d'utiliser ::toupper au lieu de std::toupper . C'est-à-dire toupper défini dans l'espace de noms global, au lieu de celui défini dans l'espace de noms std .

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);

son fonctionnement: http://ideone.com/XURh7

raison pour laquelle votre code ne fonctionne pas : il y a une autre fonction surchargée toupper dans l'Espace-nom std qui pose problème lors de la résolution du nom, car le compilateur est incapable de décider à quelle surcharge vous faites référence, quand vous passez simplement std::toupper . C'est pourquoi le compilateur dit unresolved overloaded function type dans le message d'erreur, qui indique la présence de surcharge(s).

pour aider le compilateur à résoudre la surcharge correcte, vous devez lancer std::toupper comme

(int (*)(int))std::toupper

, c'est-à-dire que le suivant fonctionnerait:

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

vérifiez vous-même: http://ideone.com/8A6iV

63
répondu Nawaz 2011-08-20 16:02:56

problème

std::transform(
    s.begin(),
    s.end(),
    std::back_inserter(out),
    std::toupper
);

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

c'est une erreur trompeuse; la partie intéressante n'est pas qu'il n'y a pas de" fonction d'appariement "pour l'appel, mais pourquoi il n'y a pas de fonction d'appariement.

le pourquoi est que vous passez une référence de fonction d'un" <unresolved overloaded function type> " comme argument, et GCC préfère se tromper sur l'appel plutôt que sur cette panne de résolution de surcharge.


explication

tout d'Abord, vous devriez considérer comment la bibliothèque C est héritée en C++. <ctype.h> a une fonction int toupper(int) .

c++ hérite de ceci:

[n3290: 21.7/1]: Tables 74, 75, 76, 77, 78, et 79 décrivent les en-têtes <cctype> , <cwctype> , <cstring> , <cwchar> , <cstdlib> (character conversions), et <cuchar> , respectivement.

[n3290: 21.7/2]: le contenu de ces en-têtes doit être le même que la Bibliothèque Standard C-têtes <ctype.h> , <wctype.h> , <string.h> , <wchar.h> , et <stdlib.h> et le code l'en-tête <uchar.h> , respectivement [..]

[n3290: 17.6.1.2/6]: Les Noms qui sont définis comme des fonctions dans C doivent sont définies comme des fonctions le C++ de la bibliothèque standard.

mais en utilisant <ctype.h> est déprécié:

[n3290: C.3.1/1]: pour la compatibilité avec la bibliothèque Standard C, la La bibliothèque standard C++ fournit les en-têtes 18 C( D. 5), mais leur utilisation est déconseillé en C++.

et la façon d'accéder au C toupper est à travers L'en-tête de rétrocompatibilité C++ <cctype> . Pour de tels en-têtes, le contenu est-ce que est déplacé ou copié (selon votre implémentation) dans l'espace de nom std :

[n3290: 17.6.1.2/4]: [..] Dans la bibliothèque standard C++, cependant, les déclarations (sauf pour les noms qui sont définis comme des macros en C) sont espace de noms de la portée (3.3.6) de l'espace de noms std. il est non spécifié si ces noms sont d'abord déclarés dans l'espace de noms global portée et sont ensuite injectés dans namespace std par explicit en utilisant-les déclarations (7.3.3).

mais la bibliothèque C++ introduit aussi un nouveau modèle de fonction spécifique à une locale dans l'en-tête <locale> , c'est-à-dire aussi appelé toupper (bien sûr, dans l'Espace-nom std ):

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

Ainsi, lorsque vous utilisez std::toupper , il y a deux surcharges au choix. Puisque vous n'avez pas dit à GCC quelle fonction vous souhaitez utiliser, la surcharge ne peut pas être résolue et votre appel à std::transform ne peut pas être complété.


disparité

or, L'OP de cette question originale n'a pas rencontré ce problème. Il n'avait probablement pas la version locale de std::toupper dans scope, mais là encore vous n'avez pas #include <locale> non plus!

Toutefois:

[n3290: 17.6.5.2]: un en-tête C++ peut inclure d'autres en-têtes C++.

donc il juste comme cela arrive que soit votre <iostream> ou votre <algorithm> , ou des en-têtes que ces en-têtes comprennent, ou des en-têtes que ces en-têtes comprennent (etc), conduire à l'inclusion de <locale> sur votre mise en œuvre.


Solution

il y a deux solutions de rechange.

  1. vous pouvez fournir une clause de conversion pour forcer le pointeur de fonction en se référant à la surcharge que vous souhaitez utiliser:

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       (int (*)(int))std::toupper  // specific overload requested
    );
    
  2. vous pouvez supprimer la version locale de la surcharge définie en utilisant explicitement le global toupper :

    std::transform(
       s.begin(),
       s.end(),
       std::back_inserter(out),
       ::toupper                  // global scope
    );
    

    cependant, rappelons que la disponibilité de cette fonction dans <cctype> est indéterminée ( [17.6.1.2/4] ), et l'utilisation de <ctype.h> est dépréciée ( [C.3.1/1] ).

    donc, ce n'est pas l'option que je recommande.


( Note: je méprise les crochets d'angle d'écriture comme s'ils faisaient partie des noms d'en - tête-ils font partie de la syntaxe #include , pas noms d'en-tête - mais je l'ai fait ici pour la cohérence avec les citations de FDIS; et, pour être honnête, il est plus clair...)

40
répondu Lightness Races in Orbit 2011-08-20 13:54:40