web-dev-qa-db-fra.com

std :: transform () et toupper (), pas de fonction correspondante

J'ai essayé le code de cette question C++ std :: transform () et toupper () ..pourquoi cela échoue-t-il?

#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, cela aurait dû fonctionner car c'est l'un des exemples dans le livre de Josuttis, mais il ne compile pas http://ideone.com/aYnfv .

Pourquoi le CCG 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 j'ai râté quelque chose? Est-ce un problème lié à GCC?

56
Stack Overeem

Utilisez simplement ::toupper au lieu de std::toupper. Autrement dit, toupper défini dans l'espace de noms global, au lieu de celui défini dans std espace de noms.

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 existe une autre fonction surchargée toupper dans l'espace de noms std qui pose problème lors de la résolution du nom, car le compilateur n'est pas en mesure de décider à quelle surcharge vous faites référence , lorsque 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).

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

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

Autrement dit, les éléments suivants fonctionneraient:

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

69
Nawaz

Problème

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

pas de fonction correspondante pour l'appel à ‘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>)

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

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 l'erreur sur le appeler plutôt que sur cet échec de résolution de surcharge.


Explication

Tout d'abord, vous devez 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]: Les tableaux 74, 75, 76, 77, 78 et 79 décrivent les en-têtes <cctype>, <cwctype>, <cstring>, <cwchar>, <cstdlib> (Conversions de caractères) et <cuchar>, Respectivement.

[n3290: 21.7/2]: Le contenu de ces en-têtes doit être le même que celui des en-têtes standard de la bibliothèque C <ctype.h>, <wctype.h>, <string.h>, <wchar.h> Et <stdlib.h> Et l'en-tête C Unicode TR <uchar.h>, Respectivement [..]

[n3290: 17.6.1.2/6]: Les noms définis en tant que fonctions en C doivent être définis en tant que fonctions dans la bibliothèque standard C++.

Mais l'utilisation de <ctype.h> Est déconseillée:

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

Et la façon d'accéder au C toupper est via l'en-tête de compatibilité descendante C++ <cctype>. Pour ces en-têtes, le contenu est déplacé ou copié (selon votre implémentation) dans l'espace de noms std:

[n3290: 17.6.1.2/4]: [..] Dans la bibliothèque standard C++, cependant, les déclarations (à l'exception des noms définis comme macros en C) sont dans la portée de l'espace de noms (3.3.6) de l'espace de noms std. Il n'est pas spécifié si ces noms sont d'abord déclarés dans la portée de l'espace de noms global puis injectés dans l'espace de noms std par des déclarations explicites d'utilisation (7.3.3).

Mais la bibliothèque C++ introduit également un nouveau modèle de fonction spécifique aux paramètres régionaux dans l'en-tête <locale>, C'est-à-dire également appelé toupper (bien sûr, dans l'espace de noms std):

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

Ainsi, lorsque vous utilisez std::toupper, Vous avez le choix deux surcharges. Comme vous n'avez pas indiqué à GCC la fonction que vous souhaitez utiliser, la surcharge ne peut pas être résolue et votre appel à std::transform Ne peut pas être terminé.


Disparité

Maintenant, l'OP de cette question d'origine n'a pas rencontré ce problème. Il n'avait probablement pas la version locale de std::toupper Dans le champ d'application, mais là encore, vous n'aviez pas non plus #include <locale>!

Pourtant:

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

Il se passe donc juste ce qui se passe que votre <iostream> Ou votre <algorithm>, Ou les en-têtes que ces en-têtes incluent, ou les en-têtes que ces en-têtes incluent (etc), conduisent à l'inclusion de <locale> dans votre implémentation.


Solution

Il existe deux solutions à cela.

  1. Vous pouvez fournir une clause de conversion pour contraindre le pointeur de fonction à faire référence à 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 l'ensemble de surcharge en utilisant explicitement le global toupper:

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

    Cependant, rappelez-vous que la disponibilité ou non de cette fonction dans <cctype> N'est pas spécifiée ([17.6.1.2/4]) Et l'utilisation de <ctype.h> Est déconseillée ([C.3.1/1]).

    Ce n'est donc pas l'option que je recommanderais.


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

45